mirror of
https://github.com/debauchee/barrier.git
synced 2026-07-16 11:01:53 +08:00
Started over.
This commit is contained in:
265
synergy/CClient.cpp
Normal file
265
synergy/CClient.cpp
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "CClient.h"
|
||||
#include "CInputPacketStream.h"
|
||||
#include "COutputPacketStream.h"
|
||||
#include "CProtocolUtil.h"
|
||||
#include "CTimerThread.h"
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// CClient
|
||||
//
|
||||
|
||||
CClient::CClient(const CString& clientName) :
|
||||
m_name(clientName)
|
||||
{
|
||||
}
|
||||
|
||||
CClient::~CClient()
|
||||
{
|
||||
}
|
||||
|
||||
#include "CTCPSocket.h"
|
||||
void CClient::run(const CNetworkAddress& serverAddress)
|
||||
{
|
||||
std::auto_ptr<ISocket> socket;
|
||||
std::auto_ptr<IInputStream> input;
|
||||
std::auto_ptr<IOutputStream> 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<KeyID>(id),
|
||||
reinterpret_cast<KeyModifierMask>(mask));
|
||||
}
|
||||
|
||||
void CClient::onKeyRepeat()
|
||||
{
|
||||
SInt32 id, mask, count;
|
||||
CProtocolUtil::readf(m_input, kMsgDKeyRepeat + 4, &id, &mask, &count);
|
||||
m_screen->onKeyRepeat(reinterpret_cast<KeyID>(id),
|
||||
reinterpret_cast<KeyModifierMask>(mask),
|
||||
count);
|
||||
}
|
||||
|
||||
void CClient::onKeyUp()
|
||||
{
|
||||
SInt32 id, mask;
|
||||
CProtocolUtil::readf(m_input, kMsgDKeyUp + 4, &id, &mask);
|
||||
m_screen->onKeyUp(reinterpret_cast<KeyID>(id),
|
||||
reinterpret_cast<KeyModifierMask>(mask));
|
||||
}
|
||||
|
||||
void CClient::onMouseDown()
|
||||
{
|
||||
SInt32 id;
|
||||
CProtocolUtil::readf(m_input, kMsgDMouseDown + 4, &id);
|
||||
m_screen->onMouseDown(reinterpret_cast<ButonID>(id));
|
||||
}
|
||||
|
||||
void CClient::onMouseUp()
|
||||
{
|
||||
SInt32 id;
|
||||
CProtocolUtil::readf(m_input, kMsgDMouseUp + 4, &id);
|
||||
m_screen->onMouseUp(reinterpret_cast<ButonID>(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);
|
||||
}
|
||||
46
synergy/CClient.h
Normal file
46
synergy/CClient.h
Normal file
@@ -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
|
||||
109
synergy/CInputPacketStream.cpp
Normal file
109
synergy/CInputPacketStream.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "CInputPacketStream.h"
|
||||
#include "CLock.h"
|
||||
#include <assert.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() 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);
|
||||
}
|
||||
|
||||
33
synergy/CInputPacketStream.h
Normal file
33
synergy/CInputPacketStream.h
Normal file
@@ -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
|
||||
|
||||
56
synergy/COutputPacketStream.cpp
Normal file
56
synergy/COutputPacketStream.cpp
Normal file
@@ -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<const UInt8*>(buffer);
|
||||
while (count2 > 0) {
|
||||
UInt32 n = getStream()->write(cbuffer, count2);
|
||||
cbuffer += n;
|
||||
count2 -= n;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void COutputPacketStream::flush() throw(XIO)
|
||||
{
|
||||
getStream()->flush();
|
||||
}
|
||||
|
||||
21
synergy/COutputPacketStream.h
Normal file
21
synergy/COutputPacketStream.h
Normal file
@@ -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
|
||||
336
synergy/CProtocolUtil.cpp
Normal file
336
synergy/CProtocolUtil.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include "CProtocolUtil.h"
|
||||
#include "IInputStream.h"
|
||||
#include "IOutputStream.h"
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
//
|
||||
// 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<UInt32>(buffer[0]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// 2 byte integer
|
||||
*v = (static_cast<UInt32>(buffer[0]) << 8) |
|
||||
static_cast<UInt32>(buffer[1]);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// 4 byte integer
|
||||
*v = (static_cast<UInt32>(buffer[0]) << 24) |
|
||||
(static_cast<UInt32>(buffer[1]) << 16) |
|
||||
(static_cast<UInt32>(buffer[2]) << 8) |
|
||||
static_cast<UInt32>(buffer[3]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 's': {
|
||||
assert(len == 0);
|
||||
|
||||
// read the string length
|
||||
UInt8 buffer[128];
|
||||
read(stream, buffer, 4);
|
||||
UInt32 len = (static_cast<UInt32>(buffer[0]) << 24) |
|
||||
(static_cast<UInt32>(buffer[1]) << 16) |
|
||||
(static_cast<UInt32>(buffer[2]) << 8) |
|
||||
static_cast<UInt32>(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<UInt8*>(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<UInt8>(v & 0xff);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// 2 byte integer
|
||||
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
||||
*dst++ = static_cast<UInt8>( v & 0xff);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// 4 byte integer
|
||||
*dst++ = static_cast<UInt8>((v >> 24) & 0xff);
|
||||
*dst++ = static_cast<UInt8>((v >> 16) & 0xff);
|
||||
*dst++ = static_cast<UInt8>((v >> 8) & 0xff);
|
||||
*dst++ = static_cast<UInt8>( 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<UInt8>((len >> 24) & 0xff);
|
||||
*dst++ = static_cast<UInt8>((len >> 16) & 0xff);
|
||||
*dst++ = static_cast<UInt8>((len >> 8) & 0xff);
|
||||
*dst++ = static_cast<UInt8>( 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<UInt8*>(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";
|
||||
}
|
||||
53
synergy/CProtocolUtil.h
Normal file
53
synergy/CProtocolUtil.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef CPROTOCOLUTIL_H
|
||||
#define CPROTOCOLUTIL_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "XIO.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
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
|
||||
|
||||
89
synergy/CScreenMap.cpp
Normal file
89
synergy/CScreenMap.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "CScreenMap.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// 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];
|
||||
}
|
||||
46
synergy/CScreenMap.h
Normal file
46
synergy/CScreenMap.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef CSCREENMAP_H
|
||||
#define CSCREENMAP_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "CString.h"
|
||||
#include <map>
|
||||
|
||||
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<CString, CCell> CCellMap;
|
||||
|
||||
CCellMap m_map;
|
||||
};
|
||||
|
||||
#endif
|
||||
849
synergy/CServer.cpp
Normal file
849
synergy/CServer.cpp
Normal file
@@ -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 <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
|
||||
//
|
||||
// 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<CServer>(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<SInt32>(0.5 + y *
|
||||
static_cast<double>(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<SInt32>(0.5 + x *
|
||||
static_cast<double>(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<IListenSocket> 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<CServer>(
|
||||
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<ISocket> socket(reinterpret_cast<ISocket*>(vsocket));
|
||||
|
||||
// add this thread to the list of threads to cancel. remove from
|
||||
// list in d'tor.
|
||||
CCleanupNote cleanupNote(this);
|
||||
|
||||
CString name("<unknown>");
|
||||
try {
|
||||
// get the input and output streams
|
||||
IInputStream* srcInput = socket->getInputStream();
|
||||
IOutputStream* srcOutput = socket->getOutputStream();
|
||||
std::auto_ptr<IInputStream> input;
|
||||
std::auto_ptr<IOutputStream> 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<IServerProtocol> protocol;
|
||||
std::auto_ptr<CConnectionNote> 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
|
||||
}
|
||||
158
synergy/CServer.h
Normal file
158
synergy/CServer.h
Normal file
@@ -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 <list>
|
||||
#include <map>
|
||||
|
||||
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<CThread*> CThreadList;
|
||||
typedef std::map<CString, CScreenInfo*> CScreenList;
|
||||
|
||||
CMutex m_mutex;
|
||||
CCondVar<bool> 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
|
||||
72
synergy/CServerProtocol.cpp
Normal file
72
synergy/CServerProtocol.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include "CServerProtocol.h"
|
||||
#include "CServerProtocol1_0.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "IOutputStream.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
58
synergy/CServerProtocol.h
Normal file
58
synergy/CServerProtocol.h
Normal file
@@ -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
|
||||
157
synergy/CServerProtocol1_0.cpp
Normal file
157
synergy/CServerProtocol1_0.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "CServerProtocol1_0.h"
|
||||
#include "CServer.h"
|
||||
#include "CProtocolUtil.h"
|
||||
#include "ProtocolTypes.h"
|
||||
#include "IInputStream.h"
|
||||
#include <string.h>
|
||||
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
37
synergy/CServerProtocol1_0.h
Normal file
37
synergy/CServerProtocol1_0.h
Normal file
@@ -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
|
||||
27
synergy/CTCPSocketFactory.cpp
Normal file
27
synergy/CTCPSocketFactory.cpp
Normal file
@@ -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;
|
||||
}
|
||||
20
synergy/CTCPSocketFactory.h
Normal file
20
synergy/CTCPSocketFactory.h
Normal file
@@ -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
|
||||
363
synergy/CXWindowsPrimaryScreen.cpp
Normal file
363
synergy/CXWindowsPrimaryScreen.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "CXWindowsPrimaryScreen.h"
|
||||
#include "CServer.h"
|
||||
#include "CThread.h"
|
||||
#include "TMethodJob.h"
|
||||
#include <assert.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
//
|
||||
// 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<CXWindowsPrimaryScreen>(
|
||||
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<KeyID>(::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<ButtonID>(button);
|
||||
else
|
||||
return kButtonNone;
|
||||
}
|
||||
43
synergy/CXWindowsPrimaryScreen.h
Normal file
43
synergy/CXWindowsPrimaryScreen.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef CXWINDOWSPRIMARYSCREEN_H
|
||||
#define CXWINDOWSPRIMARYSCREEN_H
|
||||
|
||||
#include "KeyTypes.h"
|
||||
#include "MouseTypes.h"
|
||||
#include "IPrimaryScreen.h"
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
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
|
||||
73
synergy/IPrimaryScreen.h
Normal file
73
synergy/IPrimaryScreen.h
Normal file
@@ -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
|
||||
71
synergy/ISecondaryScreen.h
Normal file
71
synergy/ISecondaryScreen.h
Normal file
@@ -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
|
||||
47
synergy/IServerProtocol.h
Normal file
47
synergy/IServerProtocol.h
Normal file
@@ -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
|
||||
21
synergy/ISocketFactory.h
Normal file
21
synergy/ISocketFactory.h
Normal file
@@ -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
|
||||
24
synergy/KeyTypes.h
Normal file
24
synergy/KeyTypes.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef KEYTYPES_H
|
||||
#define KEYTYPES_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
|
||||
// type to hold a key identifier
|
||||
typedef UInt32 KeyID;
|
||||
|
||||
// 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
|
||||
44
synergy/Makefile
Normal file
44
synergy/Makefile
Normal file
@@ -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)
|
||||
|
||||
15
synergy/MouseTypes.h
Normal file
15
synergy/MouseTypes.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef MOUSETYPES_H
|
||||
#define MOUSETYPES_H
|
||||
|
||||
#include "BasicTypes.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 kButtonMiddle = 2;
|
||||
static const ButtonID kButtonRight = 3;
|
||||
|
||||
#endif
|
||||
38
synergy/ProtocolTypes.h
Normal file
38
synergy/ProtocolTypes.h
Normal file
@@ -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
|
||||
|
||||
37
synergy/XSynergy.cpp
Normal file
37
synergy/XSynergy.cpp
Normal file
@@ -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";
|
||||
}
|
||||
|
||||
34
synergy/XSynergy.h
Normal file
34
synergy/XSynergy.h
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user