mirror of https://github.com/debauchee/barrier.git
initial revision of synergy. currently semi-supports X windows
on unix, but client screens don't simulate events other than mouse move. also not supporting clipboard at all yet and the main app is just a temporary framework to test with. must clean up protocol and communication.
This commit is contained in:
commit
900b075e3a
|
@ -0,0 +1,33 @@
|
|||
#ifndef BASICTYPES_H
|
||||
#define BASICTYPES_H
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#define CONFIG_PLATFORM_LINUX
|
||||
#define CONFIG_TYPES_X11
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef int8_t SInt8;
|
||||
typedef int16_t SInt16;
|
||||
typedef int32_t SInt32;
|
||||
typedef int64_t SInt64;
|
||||
|
||||
typedef uint8_t UInt8;
|
||||
typedef uint16_t UInt16;
|
||||
typedef uint32_t UInt32;
|
||||
typedef uint64_t UInt64;
|
||||
|
||||
#else
|
||||
|
||||
#error unsupported platform
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
#include "CClient.h"
|
||||
#include "CString.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "IScreen.h"
|
||||
#include "ISocket.h"
|
||||
#include "CMessageSocket.h"
|
||||
#include "CSocketFactory.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "CEvent.h"
|
||||
#include "CTrace.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CClient
|
||||
//
|
||||
|
||||
CClient::CClient(IScreen* screen) : m_screen(screen),
|
||||
m_socket(NULL)
|
||||
{
|
||||
assert(m_screen != NULL);
|
||||
assert(!m_screen->getName().empty());
|
||||
}
|
||||
|
||||
CClient::~CClient()
|
||||
{
|
||||
assert(m_socket == NULL);
|
||||
}
|
||||
|
||||
void CClient::run(const CString& hostname)
|
||||
{
|
||||
assert(m_socket == NULL);
|
||||
|
||||
try {
|
||||
// create socket and
|
||||
m_socket = CSOCKETFACTORY->create();
|
||||
m_socket->setWriteJob(new TMethodJob<CClient>(this,
|
||||
&CClient::onConnect));
|
||||
TRACE(("connecting to %s...", hostname.c_str()));
|
||||
m_socket->connect(hostname, 40001); // CProtocol::kDefaultPort
|
||||
|
||||
bool m_done = false; // FIXME
|
||||
|
||||
IEventQueue* queue = CEQ;
|
||||
while (!m_done) {
|
||||
// wait for connection, network messages, and events
|
||||
queue->wait(-1.0);
|
||||
|
||||
// handle events
|
||||
while (!queue->isEmpty()) {
|
||||
// get the next event
|
||||
CEvent event;
|
||||
queue->pop(&event);
|
||||
|
||||
// handle it
|
||||
switch (event.m_any.m_type) {
|
||||
case CEventBase::kScreenSize: {
|
||||
sendScreenSize();
|
||||
break;
|
||||
}
|
||||
|
||||
case CEventBase::kNull:
|
||||
case CEventBase::kKeyDown:
|
||||
case CEventBase::kKeyRepeat:
|
||||
case CEventBase::kKeyUp:
|
||||
case CEventBase::kMouseDown:
|
||||
case CEventBase::kMouseUp:
|
||||
case CEventBase::kMouseMove:
|
||||
case CEventBase::kMouseWheel:
|
||||
// FIXME -- other cases
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete m_socket;
|
||||
m_socket = NULL;
|
||||
}
|
||||
|
||||
catch (...) {
|
||||
delete m_socket;
|
||||
m_socket = NULL;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::onConnect()
|
||||
{
|
||||
TRACE(("connected"));
|
||||
|
||||
// say hello
|
||||
const CString name(m_screen->getName());
|
||||
char buf[512];
|
||||
memcpy(buf, "SYNERGY\000\001", 9);
|
||||
buf[9] = static_cast<char>(name.length());
|
||||
memcpy(buf + 10, name.c_str(), name.length());
|
||||
m_socket->write(buf, 10 + name.length());
|
||||
|
||||
// handle messages
|
||||
m_socket->setWriteJob(NULL);
|
||||
m_socket = new CMessageSocket(m_socket);
|
||||
m_socket->setReadJob(new TMethodJob<CClient>(this, &CClient::onRead));
|
||||
}
|
||||
|
||||
void CClient::onRead()
|
||||
{
|
||||
char buf[512];
|
||||
SInt32 n = m_socket->read(buf, sizeof(buf));
|
||||
if (n == -1) {
|
||||
// disconnect
|
||||
TRACE(("hangup"));
|
||||
}
|
||||
else if (n > 0) {
|
||||
TRACE(("msg: 0x%02x length %d", buf[0], n));
|
||||
switch (buf[0]) {
|
||||
case '\002':
|
||||
TRACE((" open"));
|
||||
|
||||
// open the screen
|
||||
m_screen->open(buf[1] != 0);
|
||||
|
||||
// send initial size
|
||||
sendScreenSize();
|
||||
break;
|
||||
|
||||
case '\003':
|
||||
TRACE((" close"));
|
||||
m_screen->close();
|
||||
break;
|
||||
|
||||
case '\004': {
|
||||
const SInt32 x = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
const SInt32 y = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[5]) << 24) +
|
||||
(static_cast<UInt32>(buf[6]) << 16) +
|
||||
(static_cast<UInt32>(buf[7]) << 8) +
|
||||
(static_cast<UInt32>(buf[8]) ));
|
||||
TRACE((" enter: %d,%d", x, y));
|
||||
m_screen->enterScreen(x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\005':
|
||||
TRACE((" leave"));
|
||||
m_screen->leaveScreen();
|
||||
break;
|
||||
|
||||
case '\007': {
|
||||
const KeyID k = static_cast<KeyID>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
TRACE((" key down: %d", k));
|
||||
m_screen->onKeyDown(k);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\010': {
|
||||
const KeyID k = static_cast<KeyID>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
const SInt32 n = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[5]) << 24) +
|
||||
(static_cast<UInt32>(buf[6]) << 16) +
|
||||
(static_cast<UInt32>(buf[7]) << 8) +
|
||||
(static_cast<UInt32>(buf[8]) ));
|
||||
TRACE((" key repeat: %d x%d", k, n));
|
||||
m_screen->onKeyRepeat(k, n);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\011': {
|
||||
const KeyID k = static_cast<KeyID>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
TRACE((" key up: %d", k));
|
||||
m_screen->onKeyUp(k);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\012': {
|
||||
const KeyToggleMask m = static_cast<KeyToggleMask>(
|
||||
(static_cast<UInt32>(buf[1]) << 8) +
|
||||
(static_cast<UInt32>(buf[2]) ));
|
||||
TRACE((" key toggle: 0x%04x", m));
|
||||
m_screen->onKeyToggle(m);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\013': {
|
||||
const ButtonID b = static_cast<ButtonID>(
|
||||
static_cast<UInt32>(buf[1]));
|
||||
TRACE((" mouse down: %d", b));
|
||||
m_screen->onMouseDown(b);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\014': {
|
||||
const ButtonID b = static_cast<ButtonID>(
|
||||
static_cast<UInt32>(buf[1]));
|
||||
TRACE((" mouse up: %d", b));
|
||||
m_screen->onMouseUp(b);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\015': {
|
||||
const SInt32 x = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
const SInt32 y = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[5]) << 24) +
|
||||
(static_cast<UInt32>(buf[6]) << 16) +
|
||||
(static_cast<UInt32>(buf[7]) << 8) +
|
||||
(static_cast<UInt32>(buf[8]) ));
|
||||
TRACE((" mouse move: %d,%d", x, y));
|
||||
m_screen->onMouseMove(x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\016': {
|
||||
const SInt32 n = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
TRACE((" mouse wheel: %d", n));
|
||||
m_screen->onMouseWheel(n);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\017': {
|
||||
TRACE((" screen saver: %s", buf[1] ? "on" : "off"));
|
||||
m_screen->onScreenSaver(buf[1] != 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\020': {
|
||||
const SInt32 x = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
const SInt32 y = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[5]) << 24) +
|
||||
(static_cast<UInt32>(buf[6]) << 16) +
|
||||
(static_cast<UInt32>(buf[7]) << 8) +
|
||||
(static_cast<UInt32>(buf[8]) ));
|
||||
TRACE((" warp: %d,%d", x, y));
|
||||
m_screen->warpCursor(x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TRACE((" unknown message"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CClient::sendScreenSize()
|
||||
{
|
||||
// get the size
|
||||
SInt32 w, h;
|
||||
m_screen->getSize(&w, &h);
|
||||
|
||||
// send it
|
||||
char buf[9];
|
||||
memcpy(buf, "\201", 1);
|
||||
buf[1] = static_cast<char>((w >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((w >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((w >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(w & 0xff);
|
||||
buf[5] = static_cast<char>((h >> 24) & 0xff);
|
||||
buf[6] = static_cast<char>((h >> 16) & 0xff);
|
||||
buf[7] = static_cast<char>((h >> 8) & 0xff);
|
||||
buf[8] = static_cast<char>(h & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef CCLIENT_H
|
||||
#define CCLIENT_H
|
||||
|
||||
#include "IClient.h"
|
||||
|
||||
class IScreen;
|
||||
class ISocket;
|
||||
|
||||
class CClient : public IClient {
|
||||
public:
|
||||
CClient(IScreen* screen);
|
||||
virtual ~CClient();
|
||||
|
||||
// IClient overrides
|
||||
virtual void run(const CString& hostname);
|
||||
|
||||
private:
|
||||
void onConnect();
|
||||
void onRead();
|
||||
|
||||
void sendScreenSize();
|
||||
|
||||
private:
|
||||
IScreen* m_screen;
|
||||
ISocket* m_socket;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef CEVENT_H
|
||||
#define CEVENT_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "MouseTypes.h"
|
||||
|
||||
class ISocket;
|
||||
|
||||
class CEventBase {
|
||||
public:
|
||||
enum EType {
|
||||
kNull,
|
||||
kKeyDown,
|
||||
kKeyRepeat,
|
||||
kKeyUp,
|
||||
kMouseDown,
|
||||
kMouseUp,
|
||||
kMouseMove,
|
||||
kMouseWheel,
|
||||
kScreenSize
|
||||
};
|
||||
|
||||
EType m_type;
|
||||
};
|
||||
|
||||
class CEventKey : public CEventBase {
|
||||
public:
|
||||
KeyID m_key;
|
||||
SInt32 m_count;
|
||||
};
|
||||
|
||||
class CEventMouse : public CEventBase {
|
||||
public:
|
||||
ButtonID m_button;
|
||||
SInt32 m_x; // or wheel delta
|
||||
SInt32 m_y;
|
||||
};
|
||||
|
||||
class CEventSize : public CEventBase {
|
||||
public:
|
||||
SInt32 m_w;
|
||||
SInt32 m_h;
|
||||
};
|
||||
|
||||
class CEvent {
|
||||
public:
|
||||
union {
|
||||
public:
|
||||
CEventBase m_any;
|
||||
CEventKey m_key;
|
||||
CEventMouse m_mouse;
|
||||
CEventSize m_size;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "CEventQueue.h"
|
||||
|
||||
//
|
||||
// IEventQueue
|
||||
//
|
||||
|
||||
IEventQueue* IEventQueue::s_instance = NULL;
|
||||
|
||||
IEventQueue::IEventQueue()
|
||||
{
|
||||
assert(s_instance == NULL);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
IEventQueue::~IEventQueue()
|
||||
{
|
||||
s_instance = NULL;
|
||||
}
|
||||
|
||||
IEventQueue* IEventQueue::getInstance()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CEventQueue
|
||||
//
|
||||
|
||||
CEventQueue::CEventQueue()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CEventQueue::~CEventQueue()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CEventQueue::pop(CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
|
||||
// wait for an event
|
||||
while (isEmpty())
|
||||
wait(-1.0);
|
||||
|
||||
// lock the queue, extract an event, then unlock
|
||||
lock();
|
||||
*event = m_queue.front();
|
||||
m_queue.pop_front();
|
||||
unlock();
|
||||
}
|
||||
|
||||
void CEventQueue::push(const CEvent* event)
|
||||
{
|
||||
// put the event at the end of the queue and signal that the queue
|
||||
// is not empty
|
||||
lock();
|
||||
m_queue.push_back(*event);
|
||||
signalNotEmpty();
|
||||
unlock();
|
||||
}
|
||||
|
||||
bool CEventQueue::isEmpty()
|
||||
{
|
||||
lock();
|
||||
bool e = m_queue.empty();
|
||||
unlock();
|
||||
|
||||
// if queue is empty then poll to see if more stuff is ready to go
|
||||
// on the queue and check again if the queue is empty.
|
||||
if (e) {
|
||||
wait(0.0);
|
||||
lock();
|
||||
e = m_queue.empty();
|
||||
unlock();
|
||||
}
|
||||
return e;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef CEVENTQUEUE_H
|
||||
#define CEVENTQUEUE_H
|
||||
|
||||
#include "IEventQueue.h"
|
||||
#include "CEvent.h"
|
||||
#include <list>
|
||||
|
||||
class CEventQueue : public IEventQueue {
|
||||
public:
|
||||
CEventQueue();
|
||||
virtual ~CEventQueue();
|
||||
|
||||
// IEventQueue overrides
|
||||
virtual void wait(double timeout) = 0;
|
||||
virtual void pop(CEvent*);
|
||||
virtual void push(const CEvent*);
|
||||
virtual bool isEmpty();
|
||||
|
||||
protected:
|
||||
// signal the queue not-empty condition. this should cause wait()
|
||||
// to stop waiting.
|
||||
virtual void signalNotEmpty() = 0;
|
||||
|
||||
// lock the queue mutex
|
||||
virtual void lock() = 0;
|
||||
|
||||
// unlock the queue mutex
|
||||
virtual void unlock() = 0;
|
||||
|
||||
private:
|
||||
typedef std::list<CEvent> List;
|
||||
|
||||
List m_queue;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,153 @@
|
|||
#include "CMessageSocket.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CTrace.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
//
|
||||
// CMessageSocket
|
||||
//
|
||||
|
||||
CMessageSocket::CMessageSocket(ISocket* socket) :
|
||||
m_socket(socket),
|
||||
m_buffer(NULL),
|
||||
m_size(0),
|
||||
m_capacity(0),
|
||||
m_msgSize(0)
|
||||
{
|
||||
m_socket->setReadJob(new TMethodJob<CMessageSocket>(this,
|
||||
&CMessageSocket::readJobCB));
|
||||
}
|
||||
|
||||
CMessageSocket::~CMessageSocket()
|
||||
{
|
||||
delete m_socket;
|
||||
delete[] m_buffer;
|
||||
}
|
||||
|
||||
void CMessageSocket::setWriteJob(IJob* adoptedJob)
|
||||
{
|
||||
CSocket::setWriteJob(adoptedJob);
|
||||
if (adoptedJob != NULL)
|
||||
m_socket->setWriteJob(new TMethodJob<CMessageSocket>(this,
|
||||
&CMessageSocket::writeJobCB));
|
||||
else
|
||||
m_socket->setWriteJob(NULL);
|
||||
}
|
||||
|
||||
void CMessageSocket::connect(const CString&, UInt16)
|
||||
{
|
||||
assert(0 && "connect() illegal on CMessageSocket");
|
||||
}
|
||||
|
||||
void CMessageSocket::listen(const CString&, UInt16)
|
||||
{
|
||||
assert(0 && "listen() illegal on CMessageSocket");
|
||||
}
|
||||
|
||||
ISocket* CMessageSocket::accept()
|
||||
{
|
||||
assert(0 && "accept() illegal on CMessageSocket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SInt32 CMessageSocket::read(void* buffer, SInt32 n)
|
||||
{
|
||||
// if we don't have an entire message yet then read more data
|
||||
if (m_size == 0 || m_size < m_msgSize) {
|
||||
doRead();
|
||||
}
|
||||
|
||||
// if we don't have a whole message yet then return 0
|
||||
if (m_size < m_msgSize)
|
||||
return 0;
|
||||
|
||||
// how many bytes should we return?
|
||||
if (m_msgSize - 2 < n)
|
||||
n = m_msgSize - 2;
|
||||
|
||||
// copy data
|
||||
// FIXME -- should have method for retrieving size of next message
|
||||
::memcpy(buffer, m_buffer + 2, n);
|
||||
|
||||
// discard returned message
|
||||
::memmove(m_buffer, m_buffer + m_msgSize, m_size - m_msgSize);
|
||||
m_size -= m_msgSize;
|
||||
m_msgSize = 0;
|
||||
|
||||
// get next message size
|
||||
if (m_size >= 2) {
|
||||
m_msgSize = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(m_buffer[1]) << 8) +
|
||||
(static_cast<UInt32>(m_buffer[2]) ));
|
||||
TRACE((" next message size: %d", m_msgSize));
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void CMessageSocket::write(const void* buffer, SInt32 n)
|
||||
{
|
||||
// FIXME -- no fixed size buffers
|
||||
char tmp[512];
|
||||
assert(n < (SInt32)sizeof(tmp) - 2);
|
||||
::memcpy(tmp + 2, buffer, n);
|
||||
n += 2;
|
||||
tmp[0] = static_cast<char>((n >> 8) & 0xff);
|
||||
tmp[1] = static_cast<char>(n & 0xff);
|
||||
m_socket->write(tmp, n);
|
||||
}
|
||||
|
||||
SInt32 CMessageSocket::doRead()
|
||||
{
|
||||
// if read buffer is full then grow it
|
||||
if (m_size == m_capacity) {
|
||||
// compute new capacity and allocate space
|
||||
SInt32 newCapacity = (m_capacity < 256) ? 256 : 2 * m_capacity;
|
||||
UInt8* newBuffer = new UInt8[newCapacity];
|
||||
|
||||
// cut over
|
||||
::memcpy(newBuffer, m_buffer, m_size);
|
||||
delete[] m_buffer;
|
||||
m_buffer = newBuffer;
|
||||
m_capacity = newCapacity;
|
||||
}
|
||||
|
||||
// read as much data as possible
|
||||
const SInt32 numRead = m_socket->read(m_buffer + m_size,
|
||||
m_capacity - m_size);
|
||||
TRACE(("socket %p read %d bytes", this, numRead));
|
||||
|
||||
// hangup is a special case. if buffer isn't empty then we'll
|
||||
// discard the partial message.
|
||||
if (numRead == -1)
|
||||
return numRead;
|
||||
|
||||
// get next message size
|
||||
if (m_size < 2 && m_size + numRead >= 2) {
|
||||
m_msgSize = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(m_buffer[0]) << 8) +
|
||||
(static_cast<UInt32>(m_buffer[1]) ));
|
||||
TRACE((" next message size: %d", m_msgSize));
|
||||
}
|
||||
|
||||
m_size += numRead;
|
||||
return numRead;
|
||||
}
|
||||
|
||||
void CMessageSocket::readJobCB()
|
||||
{
|
||||
if (doRead() == -1) {
|
||||
// remote side hungup. don't check for readability anymore.
|
||||
m_socket->setReadJob(NULL);
|
||||
}
|
||||
else if (m_size > 0 && m_size >= m_msgSize) {
|
||||
TRACE((" message ready"));
|
||||
runReadJob();
|
||||
}
|
||||
}
|
||||
|
||||
void CMessageSocket::writeJobCB()
|
||||
{
|
||||
runWriteJob();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef CMESSAGESOCKET_H
|
||||
#define CMESSAGESOCKET_H
|
||||
|
||||
#include "CSocket.h"
|
||||
|
||||
class CMessageSocket : public CSocket {
|
||||
public:
|
||||
CMessageSocket(ISocket* adoptedSocket);
|
||||
virtual ~CMessageSocket();
|
||||
|
||||
// ISocket overrides
|
||||
// connect(), listen(), and accept() may not be called.
|
||||
virtual void setWriteJob(IJob* adoptedJob);
|
||||
virtual void connect(const CString& hostname, UInt16 port);
|
||||
virtual void listen(const CString& hostname, UInt16 port);
|
||||
virtual ISocket* accept();
|
||||
virtual SInt32 read(void* buffer, SInt32 numBytes);
|
||||
virtual void write(const void* buffer, SInt32 numBytes);
|
||||
|
||||
private:
|
||||
SInt32 doRead();
|
||||
virtual void readJobCB();
|
||||
virtual void writeJobCB();
|
||||
|
||||
private:
|
||||
ISocket* m_socket;
|
||||
UInt8* m_buffer;
|
||||
SInt32 m_size;
|
||||
SInt32 m_capacity;
|
||||
SInt32 m_msgSize;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef CPROTOCOL_H
|
||||
#define CPROTOCOL_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
|
||||
class CProtocol {
|
||||
public:
|
||||
CProtocol();
|
||||
virtual ~CProtocol();
|
||||
|
||||
// manipulators
|
||||
|
||||
|
||||
// accessors
|
||||
|
||||
void ReadMessage(ISocket*, CMessage&) const;
|
||||
void WriteMessage(ISocket*, const CMessage&) const;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,234 @@
|
|||
#include "CScreenProxy.h"
|
||||
#include "ISocket.h"
|
||||
#include "CMessageSocket.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CTrace.h"
|
||||
//
|
||||
// CScreenProxy
|
||||
//
|
||||
|
||||
CScreenProxy::CScreenProxy(const CString& name, ISocket* socket) :
|
||||
m_name(name),
|
||||
m_socket(socket),
|
||||
m_w(0), m_h(0)
|
||||
{
|
||||
assert(!m_name.empty());
|
||||
assert(m_socket != NULL);
|
||||
|
||||
m_socket = new CMessageSocket(m_socket);
|
||||
m_socket->setReadJob(new TMethodJob<CScreenProxy>(this,
|
||||
&CScreenProxy::onRead));
|
||||
}
|
||||
|
||||
CScreenProxy::~CScreenProxy()
|
||||
{
|
||||
delete m_socket;
|
||||
}
|
||||
|
||||
void CScreenProxy::open(bool isPrimary)
|
||||
{
|
||||
char buf[2];
|
||||
memcpy(buf, "\002", 1);
|
||||
buf[1] = static_cast<char>(isPrimary ? 1 : 0);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::close()
|
||||
{
|
||||
char buf[1];
|
||||
memcpy(buf, "\003", 1);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::enterScreen(SInt32 x, SInt32 y)
|
||||
{
|
||||
char buf[9];
|
||||
memcpy(buf, "\004", 1);
|
||||
buf[1] = static_cast<char>((x >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((x >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((x >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(x & 0xff);
|
||||
buf[5] = static_cast<char>((y >> 24) & 0xff);
|
||||
buf[6] = static_cast<char>((y >> 16) & 0xff);
|
||||
buf[7] = static_cast<char>((y >> 8) & 0xff);
|
||||
buf[8] = static_cast<char>(y & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::leaveScreen()
|
||||
{
|
||||
char buf[1];
|
||||
memcpy(buf, "\005", 1);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::warpCursor(SInt32 x, SInt32 y)
|
||||
{
|
||||
char buf[9];
|
||||
memcpy(buf, "\020", 1);
|
||||
buf[1] = static_cast<char>((x >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((x >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((x >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(x & 0xff);
|
||||
buf[5] = static_cast<char>((y >> 24) & 0xff);
|
||||
buf[6] = static_cast<char>((y >> 16) & 0xff);
|
||||
buf[7] = static_cast<char>((y >> 8) & 0xff);
|
||||
buf[8] = static_cast<char>(y & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::setClipboard(const IClipboard*)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CScreenProxy::onKeyDown(KeyID k)
|
||||
{
|
||||
char buf[5];
|
||||
memcpy(buf, "\007", 1);
|
||||
buf[1] = static_cast<char>((k >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((k >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((k >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(k & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onKeyRepeat(KeyID k, SInt32 n)
|
||||
{
|
||||
char buf[9];
|
||||
memcpy(buf, "\010", 1);
|
||||
buf[1] = static_cast<char>((k >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((k >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((k >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(k & 0xff);
|
||||
buf[5] = static_cast<char>((n >> 24) & 0xff);
|
||||
buf[6] = static_cast<char>((n >> 16) & 0xff);
|
||||
buf[7] = static_cast<char>((n >> 8) & 0xff);
|
||||
buf[8] = static_cast<char>(n & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onKeyUp(KeyID k)
|
||||
{
|
||||
char buf[5];
|
||||
memcpy(buf, "\011", 1);
|
||||
buf[1] = static_cast<char>((k >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((k >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((k >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(k & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onKeyToggle(KeyToggleMask m)
|
||||
{
|
||||
char buf[3];
|
||||
memcpy(buf, "\012", 1);
|
||||
buf[1] = static_cast<char>((m >> 8) & 0xff);
|
||||
buf[2] = static_cast<char>(m & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onMouseDown(ButtonID b)
|
||||
{
|
||||
char buf[2];
|
||||
memcpy(buf, "\013", 1);
|
||||
buf[1] = static_cast<char>(b & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onMouseUp(ButtonID b)
|
||||
{
|
||||
char buf[2];
|
||||
memcpy(buf, "\014", 1);
|
||||
buf[1] = static_cast<char>(b & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onMouseMove(SInt32 x, SInt32 y)
|
||||
{
|
||||
char buf[9];
|
||||
memcpy(buf, "\015", 1);
|
||||
buf[1] = static_cast<char>((x >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((x >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((x >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(x & 0xff);
|
||||
buf[5] = static_cast<char>((y >> 24) & 0xff);
|
||||
buf[6] = static_cast<char>((y >> 16) & 0xff);
|
||||
buf[7] = static_cast<char>((y >> 8) & 0xff);
|
||||
buf[8] = static_cast<char>(y & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onMouseWheel(SInt32 n)
|
||||
{
|
||||
char buf[5];
|
||||
memcpy(buf, "\016", 1);
|
||||
buf[1] = static_cast<char>((n >> 24) & 0xff);
|
||||
buf[2] = static_cast<char>((n >> 16) & 0xff);
|
||||
buf[3] = static_cast<char>((n >> 8) & 0xff);
|
||||
buf[4] = static_cast<char>(n & 0xff);
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onScreenSaver(bool show)
|
||||
{
|
||||
char buf[2];
|
||||
memcpy(buf, "\017", 1);
|
||||
buf[1] = show ? 1 : 0;
|
||||
m_socket->write(buf, sizeof(buf));
|
||||
}
|
||||
|
||||
void CScreenProxy::onClipboardChanged()
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
CString CScreenProxy::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void CScreenProxy::getSize(SInt32* w, SInt32* h) const
|
||||
{
|
||||
assert(w != NULL);
|
||||
assert(h != NULL);
|
||||
|
||||
*w = m_w;
|
||||
*h = m_h;
|
||||
}
|
||||
|
||||
void CScreenProxy::getClipboard(IClipboard*) const
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CScreenProxy::onRead()
|
||||
{
|
||||
char buf[512];
|
||||
SInt32 n = m_socket->read(buf, sizeof(buf));
|
||||
if (n == -1) {
|
||||
// FIXME -- disconnect
|
||||
TRACE(("hangup"));
|
||||
}
|
||||
else if (n > 0) {
|
||||
switch (buf[0]) {
|
||||
case '\201':
|
||||
m_w = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[1]) << 24) +
|
||||
(static_cast<UInt32>(buf[2]) << 16) +
|
||||
(static_cast<UInt32>(buf[3]) << 8) +
|
||||
(static_cast<UInt32>(buf[4]) ));
|
||||
m_h = static_cast<SInt32>(
|
||||
(static_cast<UInt32>(buf[5]) << 24) +
|
||||
(static_cast<UInt32>(buf[6]) << 16) +
|
||||
(static_cast<UInt32>(buf[7]) << 8) +
|
||||
(static_cast<UInt32>(buf[8]) ));
|
||||
TRACE(("new size: %dx%d", m_w, m_h));
|
||||
break;
|
||||
|
||||
default:
|
||||
TRACE(("unknown message: 0x%02x, %d bytes", buf[0], n));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef CSCREENPROXY_H
|
||||
#define CSCREENPROXY_H
|
||||
|
||||
#include "IScreen.h"
|
||||
|
||||
class ISocket;
|
||||
|
||||
class CScreenProxy : public IScreen {
|
||||
public:
|
||||
CScreenProxy(const CString& name, ISocket*);
|
||||
virtual ~CScreenProxy();
|
||||
|
||||
// IScreen overrides
|
||||
virtual void open(bool);
|
||||
virtual void close();
|
||||
virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute);
|
||||
virtual void leaveScreen();
|
||||
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute);
|
||||
virtual void setClipboard(const IClipboard*);
|
||||
virtual void onScreenSaver(bool show);
|
||||
virtual void onKeyDown(KeyID);
|
||||
virtual void onKeyRepeat(KeyID, SInt32 count);
|
||||
virtual void onKeyUp(KeyID);
|
||||
virtual void onKeyToggle(KeyToggleMask);
|
||||
virtual void onMouseDown(ButtonID);
|
||||
virtual void onMouseUp(ButtonID);
|
||||
virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute);
|
||||
virtual void onMouseWheel(SInt32 delta);
|
||||
virtual void onClipboardChanged();
|
||||
virtual CString getName() const;
|
||||
virtual void getSize(SInt32* width, SInt32* height) const;
|
||||
virtual void getClipboard(IClipboard*) const;
|
||||
|
||||
private:
|
||||
void onRead();
|
||||
|
||||
private:
|
||||
CString m_name;
|
||||
ISocket* m_socket;
|
||||
SInt32 m_w, m_h;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,811 @@
|
|||
#include "CServer.h"
|
||||
#include "CEvent.h"
|
||||
#include "IEventQueue.h"
|
||||
#include "IScreen.h"
|
||||
#include "CScreenProxy.h"
|
||||
#include "ISocket.h"
|
||||
#include "CSocketFactory.h"
|
||||
#include "CMessageSocket.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CTrace.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
static const char* s_dirName[] = { "left", "right", "top", "bottom" };
|
||||
#endif
|
||||
|
||||
//
|
||||
// CServerSocketJob
|
||||
//
|
||||
|
||||
class CServerSocketJob : public IJob {
|
||||
public:
|
||||
typedef void (CServer::*ServerMethod)(ISocket*);
|
||||
|
||||
CServerSocketJob(CServer*, ServerMethod, ISocket*);
|
||||
virtual ~CServerSocketJob();
|
||||
|
||||
// IJob overrides
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
CServer* m_server;
|
||||
ServerMethod m_method;
|
||||
ISocket* m_socket;
|
||||
};
|
||||
|
||||
CServerSocketJob::CServerSocketJob(CServer* server,
|
||||
ServerMethod method, ISocket* socket) :
|
||||
m_server(server),
|
||||
m_method(method),
|
||||
m_socket(socket)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CServerSocketJob::~CServerSocketJob()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CServerSocketJob::run()
|
||||
{
|
||||
(m_server->*m_method)(m_socket);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CServer
|
||||
//
|
||||
|
||||
class XServerScreenExists { // FIXME
|
||||
public:
|
||||
XServerScreenExists(const CString&) { }
|
||||
};
|
||||
|
||||
// the width/height of the zone on the edge of the local screen that
|
||||
// will provoke a switch to a neighboring screen. this generally
|
||||
// shouldn't be changed because it effectively reduces the size of
|
||||
// the local screen's screen.
|
||||
// FIXME -- should get this from the local screen itself. it may
|
||||
// need a slightly larger zone (to avoid virtual screens) or it may
|
||||
// be able to generate off-screen coordinates to provoke the switch
|
||||
// in which case the size can be zero.
|
||||
const SInt32 CServer::s_zoneSize = 1;
|
||||
|
||||
CServer::CServer() : m_running(false), m_done(false),
|
||||
m_localScreen(NULL),
|
||||
m_activeScreen(NULL),
|
||||
m_listenHost(),
|
||||
// FIXME -- define kDefaultPort
|
||||
m_listenPort(40001/*CProtocol::kDefaultPort*/),
|
||||
m_listenSocket(NULL)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
CServer::~CServer()
|
||||
{
|
||||
assert(m_listenSocket == NULL);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CServer::setListenPort(
|
||||
const CString& hostname, UInt16 port)
|
||||
{
|
||||
m_listenHost = hostname;
|
||||
m_listenPort = port;
|
||||
}
|
||||
|
||||
void CServer::addLocalScreen(IScreen* screen)
|
||||
{
|
||||
assert(screen != NULL);
|
||||
assert(m_running == false);
|
||||
assert(m_localScreen == NULL);
|
||||
|
||||
addScreen(screen->getName(), screen);
|
||||
m_localScreen = screen;
|
||||
m_activeScreen = screen;
|
||||
|
||||
// open the screen as primary
|
||||
screen->open(true);
|
||||
}
|
||||
|
||||
void CServer::addRemoteScreen(const CString& name)
|
||||
{
|
||||
addScreen(name, NULL);
|
||||
}
|
||||
|
||||
void CServer::addScreen(const CString& name, IScreen* screen)
|
||||
{
|
||||
assert(!name.empty());
|
||||
|
||||
// cannot add a screen multiple times
|
||||
if (m_map.count(name) != 0)
|
||||
throw XServerScreenExists(name);
|
||||
|
||||
// add entry for screen in the map
|
||||
ScreenCell& cell = m_map[name];
|
||||
|
||||
// set the cell's screen
|
||||
cell.m_screen = screen;
|
||||
}
|
||||
|
||||
void CServer::removeScreen(const CString& name)
|
||||
{
|
||||
// screen must in map
|
||||
assert(!name.empty());
|
||||
assert(m_map.count(name) == 1);
|
||||
|
||||
// look up cell
|
||||
ScreenCell& cell = m_map[name];
|
||||
|
||||
// if this is the local screen then there must not be any other
|
||||
// screens and we must not be running.
|
||||
assert(cell.m_screen != m_localScreen || (m_map.size() == 1 && !m_running));
|
||||
|
||||
// if this is the active screen then warp to the local screen, or
|
||||
// set no active screen if this is the local screen.
|
||||
if (cell.m_screen == m_localScreen) {
|
||||
m_activeScreen = NULL;
|
||||
m_localScreen = NULL;
|
||||
}
|
||||
else if (cell.m_screen == m_activeScreen) {
|
||||
setActiveScreen(m_localScreen);
|
||||
}
|
||||
|
||||
// close the screen
|
||||
if (cell.m_screen)
|
||||
cell.m_screen->close();
|
||||
|
||||
// fix up map
|
||||
if (!cell.m_neighbor[kLeft].empty()) {
|
||||
assert(m_map.count(cell.m_neighbor[kLeft]) == 1);
|
||||
m_map[cell.m_neighbor[kLeft]].m_neighbor[kRight] =
|
||||
cell.m_neighbor[kRight];
|
||||
}
|
||||
if (!cell.m_neighbor[kRight].empty()) {
|
||||
assert(m_map.count(cell.m_neighbor[kRight]) == 1);
|
||||
m_map[cell.m_neighbor[kRight]].m_neighbor[kLeft] =
|
||||
cell.m_neighbor[kLeft];
|
||||
}
|
||||
if (!cell.m_neighbor[kTop].empty()) {
|
||||
assert(m_map.count(cell.m_neighbor[kTop]) == 1);
|
||||
m_map[cell.m_neighbor[kTop]].m_neighbor[kBottom] =
|
||||
cell.m_neighbor[kBottom];
|
||||
}
|
||||
if (!cell.m_neighbor[kBottom].empty()) {
|
||||
assert(m_map.count(cell.m_neighbor[kBottom]) == 1);
|
||||
m_map[cell.m_neighbor[kBottom]].m_neighbor[kTop] =
|
||||
cell.m_neighbor[kTop];
|
||||
}
|
||||
}
|
||||
|
||||
void CServer::connectEdge(
|
||||
const CString& src, EDirection srcSide,
|
||||
const CString& dst)
|
||||
{
|
||||
// check input
|
||||
assert(!src.empty());
|
||||
assert(!dst.empty());
|
||||
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||
|
||||
// both screens must exist in map
|
||||
assert(m_map.count(src) == 1);
|
||||
assert(m_map.count(dst) == 1);
|
||||
|
||||
// look up map entry
|
||||
ScreenCell& cell = m_map[src];
|
||||
|
||||
// set edge
|
||||
cell.m_neighbor[srcSide] = dst;
|
||||
|
||||
TRACE(("connect %s:%s to %s", src.c_str(),
|
||||
s_dirName[srcSide],
|
||||
cell.m_neighbor[srcSide].c_str()));
|
||||
}
|
||||
|
||||
void CServer::disconnectEdge(
|
||||
const CString& src, EDirection srcSide)
|
||||
{
|
||||
// check input
|
||||
assert(!src.empty());
|
||||
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||
assert(m_map.count(src) == 1);
|
||||
|
||||
TRACE(("disconnect %s:%s from %s", src.c_str(),
|
||||
s_dirName[srcSide],
|
||||
m_map[src].m_neighbor[srcSide].c_str()));
|
||||
|
||||
// look up map entry
|
||||
ScreenCell& cell = m_map[src];
|
||||
|
||||
// set edge
|
||||
cell.m_neighbor[srcSide] = CString();
|
||||
}
|
||||
|
||||
void CServer::run()
|
||||
{
|
||||
assert(m_running == false);
|
||||
assert(m_activeScreen != NULL);
|
||||
assert(m_activeScreen == m_localScreen);
|
||||
|
||||
// prepare socket to listen for remote screens
|
||||
// FIXME -- need m_socketFactory (creates sockets of desired type)
|
||||
// m_listenSocket = m_socketFactory->createSocket();
|
||||
m_listenSocket = CSOCKETFACTORY->create();
|
||||
m_listenSocket->setReadJob(new TMethodJob<CServer>(this,
|
||||
&CServer::newConnectionCB));
|
||||
// FIXME -- keep retrying until this works (in case of FIN_WAIT).
|
||||
// also, must clean up m_listenSocket if this method throws anywhere.
|
||||
m_listenSocket->listen(m_listenHost, m_listenPort);
|
||||
|
||||
// now running
|
||||
m_running = true;
|
||||
|
||||
// event loop
|
||||
IEventQueue* queue = CEQ;
|
||||
while (!m_done) {
|
||||
// wait for new connections, network messages, and user events
|
||||
queue->wait(-1.0);
|
||||
|
||||
// handle events
|
||||
while (!queue->isEmpty()) {
|
||||
// get the next event
|
||||
CEvent event;
|
||||
queue->pop(&event);
|
||||
|
||||
// handle it
|
||||
switch (event.m_any.m_type) {
|
||||
case CEventBase::kNull:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
case CEventBase::kKeyDown:
|
||||
case CEventBase::kKeyRepeat:
|
||||
case CEventBase::kKeyUp:
|
||||
if (!onCommandKey(&event.m_key))
|
||||
relayEvent(&event);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseDown:
|
||||
case CEventBase::kMouseUp:
|
||||
case CEventBase::kMouseWheel:
|
||||
relayEvent(&event);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseMove:
|
||||
if (m_localScreen == m_activeScreen)
|
||||
onLocalMouseMove(event.m_mouse.m_x, event.m_mouse.m_y);
|
||||
else
|
||||
onRemoteMouseMove(event.m_mouse.m_x, event.m_mouse.m_y);
|
||||
break;
|
||||
|
||||
case CEventBase::kScreenSize:
|
||||
// FIXME
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset
|
||||
m_running = false;
|
||||
m_done = false;
|
||||
|
||||
// tell screens to shutdown
|
||||
// FIXME
|
||||
|
||||
// close our socket
|
||||
delete m_listenSocket;
|
||||
m_listenSocket = NULL;
|
||||
}
|
||||
|
||||
void CServer::onClipboardChanged(IScreen*)
|
||||
{
|
||||
// FIXME -- should take screen name not screen pointer
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CServer::setActiveScreen(IScreen* screen)
|
||||
{
|
||||
// FIXME -- should take screen name not screen pointer
|
||||
assert(screen != NULL);
|
||||
assert(m_map.count(screen->getName()) == 1);
|
||||
|
||||
// ignore if no change
|
||||
if (m_activeScreen == screen)
|
||||
return;
|
||||
|
||||
// get center of screen
|
||||
SInt32 w, h;
|
||||
screen->getSize(&w, &h);
|
||||
w >>= 1;
|
||||
h >>= 1;
|
||||
|
||||
// switch
|
||||
switchScreen(screen, w, h);
|
||||
}
|
||||
|
||||
IScreen* CServer::getActiveScreen() const
|
||||
{
|
||||
return m_activeScreen;
|
||||
}
|
||||
|
||||
void CServer::relayEvent(const CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
assert(m_activeScreen != NULL);
|
||||
|
||||
// ignore attempts to relay to the local screen
|
||||
if (m_activeScreen == m_localScreen)
|
||||
return;
|
||||
|
||||
// relay the event
|
||||
switch (event->m_any.m_type) {
|
||||
case CEventBase::kNull:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
case CEventBase::kKeyDown:
|
||||
m_activeScreen->onKeyDown(event->m_key.m_key);
|
||||
break;
|
||||
|
||||
case CEventBase::kKeyRepeat:
|
||||
m_activeScreen->onKeyRepeat(event->m_key.m_key, event->m_key.m_count);
|
||||
break;
|
||||
|
||||
case CEventBase::kKeyUp:
|
||||
m_activeScreen->onKeyUp(event->m_key.m_key);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseDown:
|
||||
m_activeScreen->onMouseDown(event->m_mouse.m_button);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseUp:
|
||||
m_activeScreen->onMouseUp(event->m_mouse.m_button);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseWheel:
|
||||
m_activeScreen->onMouseWheel(event->m_mouse.m_x);
|
||||
break;
|
||||
|
||||
case CEventBase::kMouseMove:
|
||||
assert(0 && "kMouseMove relayed");
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "invalid event relayed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool CServer::onCommandKey(const CEventKey* /*keyEvent*/)
|
||||
{
|
||||
// FIXME -- strip out command keys (e.g. lock to screen, warp, etc.)
|
||||
return false;
|
||||
}
|
||||
|
||||
void CServer::onLocalMouseMove(SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(m_activeScreen == m_localScreen);
|
||||
|
||||
// ignore if locked to screen
|
||||
if (isLockedToScreen())
|
||||
return;
|
||||
|
||||
// get local screen's size
|
||||
SInt32 w, h;
|
||||
m_activeScreen->getSize(&w, &h);
|
||||
|
||||
// see if we should change screens
|
||||
EDirection dir;
|
||||
if (x < s_zoneSize) {
|
||||
x -= s_zoneSize;
|
||||
dir = kLeft;
|
||||
}
|
||||
else if (x >= w - s_zoneSize) {
|
||||
x += s_zoneSize;
|
||||
dir = kRight;
|
||||
}
|
||||
else if (y < s_zoneSize) {
|
||||
y -= s_zoneSize;
|
||||
dir = kTop;
|
||||
}
|
||||
else if (y >= h - s_zoneSize) {
|
||||
y += s_zoneSize;
|
||||
dir = kBottom;
|
||||
}
|
||||
else {
|
||||
// still on local screen
|
||||
return;
|
||||
}
|
||||
TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), s_dirName[dir]));
|
||||
|
||||
// get new screen. if no screen in that direction then ignore move.
|
||||
IScreen* newScreen = getNeighbor(m_activeScreen, dir, x, y);
|
||||
if (newScreen == NULL)
|
||||
return;
|
||||
|
||||
// remap position to account for resolution differences between screens
|
||||
mapPosition(m_activeScreen, dir, newScreen, x, y);
|
||||
|
||||
// switch screen
|
||||
switchScreen(newScreen, x, y);
|
||||
}
|
||||
|
||||
void CServer::onRemoteMouseMove(SInt32 dx, SInt32 dy)
|
||||
{
|
||||
assert(m_activeScreen != NULL);
|
||||
assert(m_activeScreen != m_localScreen);
|
||||
|
||||
// put mouse back in center of local screen's grab area
|
||||
// XXX m_localScreen->warpToCenter();
|
||||
|
||||
// save old position
|
||||
const SInt32 xOld = m_x;
|
||||
const SInt32 yOld = m_y;
|
||||
|
||||
// accumulate mouse position
|
||||
m_x += dx;
|
||||
m_y += dy;
|
||||
|
||||
// get active screen's size
|
||||
SInt32 w, h;
|
||||
m_activeScreen->getSize(&w, &h);
|
||||
|
||||
// switch screens if mouse is outside screen and not locked to screen
|
||||
IScreen* newScreen = NULL;
|
||||
if (!isLockedToScreen()) {
|
||||
// find direction of neighbor
|
||||
EDirection dir;
|
||||
if (m_x < 0)
|
||||
dir = kLeft;
|
||||
else if (m_x > w - 1)
|
||||
dir = kRight;
|
||||
else if (m_y < 0)
|
||||
dir = kTop;
|
||||
else if (m_y > h - 1)
|
||||
dir = kBottom;
|
||||
else
|
||||
newScreen = m_activeScreen;
|
||||
|
||||
// get neighbor if we should switch
|
||||
if (newScreen == NULL) {
|
||||
TRACE(("leave %s on %s", m_activeScreen->getName().c_str(),
|
||||
s_dirName[dir]));
|
||||
|
||||
SInt32 x = m_x, y = m_y;
|
||||
newScreen = getNeighbor(m_activeScreen, dir, x, y);
|
||||
|
||||
// remap position to account for resolution differences
|
||||
if (newScreen != NULL) {
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
mapPosition(m_activeScreen, dir, newScreen, m_x, m_y);
|
||||
}
|
||||
else {
|
||||
if (m_x < 0)
|
||||
m_x = 0;
|
||||
else if (m_x > w - 1)
|
||||
m_x = w - 1;
|
||||
if (m_y < 0)
|
||||
m_y = 0;
|
||||
else if (m_y > h - 1)
|
||||
m_y = h - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clamp mouse position if locked to screen
|
||||
else {
|
||||
TRACE(("clamp to %s", m_activeScreen->getName().c_str()));
|
||||
|
||||
if (m_x < 0)
|
||||
m_x = 0;
|
||||
else if (m_x > w - 1)
|
||||
m_x = w - 1;
|
||||
if (m_y < 0)
|
||||
m_y = 0;
|
||||
else if (m_y > h - 1)
|
||||
m_y = h - 1;
|
||||
}
|
||||
|
||||
// if on same screen then warp cursor
|
||||
if (newScreen == NULL || newScreen == m_activeScreen) {
|
||||
// ignore if clamped mouse didn't move
|
||||
if (m_x != xOld || m_y != yOld) {
|
||||
TRACE(("move on %s to %d,%d",
|
||||
m_activeScreen->getName().c_str(), m_x, m_y));
|
||||
m_activeScreen->onMouseMove(m_x, m_y);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise switch the screen
|
||||
else {
|
||||
switchScreen(newScreen, m_x, m_y);
|
||||
}
|
||||
}
|
||||
|
||||
bool CServer::isLockedToScreen() const
|
||||
{
|
||||
// FIXME
|
||||
return false;
|
||||
}
|
||||
|
||||
void CServer::mapPosition(
|
||||
const IScreen* src, EDirection srcSide,
|
||||
const IScreen* dst, SInt32& x, SInt32& y) const
|
||||
{
|
||||
assert(src != NULL);
|
||||
assert(dst != NULL);
|
||||
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||
|
||||
// get sizes
|
||||
SInt32 wSrc, hSrc, wDst, hDst;
|
||||
src->getSize(&wSrc, &hSrc);
|
||||
dst->getSize(&wDst, &hDst);
|
||||
|
||||
// remap
|
||||
switch (srcSide) {
|
||||
case kLeft:
|
||||
case kRight:
|
||||
assert(y >= 0 && y < hSrc);
|
||||
y = static_cast<SInt32>(0.5 + y *
|
||||
static_cast<double>(hDst - 1) / (hSrc - 1));
|
||||
break;
|
||||
|
||||
case kTop:
|
||||
case kBottom:
|
||||
assert(x >= 0 && x < wSrc);
|
||||
x = static_cast<SInt32>(0.5 + x *
|
||||
static_cast<double>(wSrc - 1) / (wSrc - 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IScreen* CServer::getNeighbor(
|
||||
const IScreen* src, EDirection dir) const
|
||||
{
|
||||
// check input
|
||||
assert(src != NULL);
|
||||
assert(dir >= kFirstDirection && dir <= kLastDirection);
|
||||
assert(m_map.count(src->getName()) == 1);
|
||||
|
||||
// look up source cell
|
||||
ScreenMap::const_iterator index = m_map.find(src->getName());
|
||||
do {
|
||||
// look up name of neighbor
|
||||
const ScreenCell& cell = index->second;
|
||||
const CString dstName(cell.m_neighbor[dir]);
|
||||
|
||||
// if nothing in that direction then return NULL
|
||||
if (dstName.empty())
|
||||
return NULL;
|
||||
|
||||
// look up neighbor cell
|
||||
assert(m_map.count(dstName) == 1);
|
||||
index = m_map.find(dstName);
|
||||
|
||||
// if no screen pointer then can't go to that neighbor so keep
|
||||
// searching in the same direction.
|
||||
#ifndef NDEBUG
|
||||
if (index->second.m_screen == NULL)
|
||||
TRACE(("skipping over unconnected screen %s", dstName.c_str()));
|
||||
#endif
|
||||
} while (index->second.m_screen == NULL);
|
||||
|
||||
return index->second.m_screen;
|
||||
}
|
||||
|
||||
IScreen* CServer::getNeighbor(
|
||||
const IScreen* src, EDirection srcSide,
|
||||
SInt32& x, SInt32& y) const
|
||||
{
|
||||
// given a position relative to src and which side of the screen we
|
||||
// left, find the screen we should move onto and where. if the
|
||||
// position is sufficiently far from src then we may cross multiple
|
||||
// screens.
|
||||
|
||||
// check input
|
||||
assert(src != NULL);
|
||||
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
||||
|
||||
// get the first neighbor
|
||||
IScreen* dst = getNeighbor(src, srcSide);
|
||||
IScreen* lastGoodScreen = dst;
|
||||
|
||||
// get the original screen's size (needed for kRight and kBottom)
|
||||
SInt32 w, h;
|
||||
src->getSize(&w, &h);
|
||||
|
||||
// find destination screen, adjusting x or y (but not both)
|
||||
switch (srcSide) {
|
||||
case kLeft:
|
||||
while (dst) {
|
||||
lastGoodScreen = dst;
|
||||
lastGoodScreen->getSize(&w, &h);
|
||||
x += w;
|
||||
if (x >= 0)
|
||||
break;
|
||||
TRACE(("skipping over screen %s", dst->getName().c_str()));
|
||||
dst = getNeighbor(lastGoodScreen, srcSide);
|
||||
}
|
||||
break;
|
||||
|
||||
case kRight:
|
||||
while (dst) {
|
||||
lastGoodScreen = dst;
|
||||
x -= w;
|
||||
lastGoodScreen->getSize(&w, &h);
|
||||
if (x < w)
|
||||
break;
|
||||
TRACE(("skipping over screen %s", dst->getName().c_str()));
|
||||
dst = getNeighbor(lastGoodScreen, srcSide);
|
||||
}
|
||||
break;
|
||||
|
||||
case kTop:
|
||||
while (dst) {
|
||||
lastGoodScreen = dst;
|
||||
lastGoodScreen->getSize(&w, &h);
|
||||
y += h;
|
||||
if (y >= 0)
|
||||
break;
|
||||
TRACE(("skipping over screen %s", dst->getName().c_str()));
|
||||
dst = getNeighbor(lastGoodScreen, srcSide);
|
||||
}
|
||||
break;
|
||||
|
||||
case kBottom:
|
||||
while (dst) {
|
||||
lastGoodScreen = dst;
|
||||
y -= h;
|
||||
lastGoodScreen->getSize(&w, &h);
|
||||
if (y < h)
|
||||
break;
|
||||
TRACE(("skipping over screen %s", dst->getName().c_str()));
|
||||
dst = getNeighbor(lastGoodScreen, srcSide);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// if entering local screen then be sure to move in far enough to
|
||||
// avoid the switching zone. if entering a side that doesn't have
|
||||
// a neighbor (i.e. an asymmetrical side) then we don't need to
|
||||
// move inwards because that side can't provoke a switch.
|
||||
if (lastGoodScreen == m_localScreen) {
|
||||
ScreenMap::const_iterator index = m_map.find(m_localScreen->getName());
|
||||
const ScreenCell& cell = index->second;
|
||||
switch (srcSide) {
|
||||
case kLeft:
|
||||
if (!cell.m_neighbor[kRight].empty() && x > w - 1 - s_zoneSize)
|
||||
x = w - 1 - s_zoneSize;
|
||||
break;
|
||||
|
||||
case kRight:
|
||||
if (!cell.m_neighbor[kLeft].empty() && x < s_zoneSize)
|
||||
x = s_zoneSize;
|
||||
break;
|
||||
|
||||
case kTop:
|
||||
if (!cell.m_neighbor[kBottom].empty() && y > h - 1 - s_zoneSize)
|
||||
y = h - 1 - s_zoneSize;
|
||||
break;
|
||||
|
||||
case kBottom:
|
||||
if (!cell.m_neighbor[kTop].empty() && y < s_zoneSize)
|
||||
y = s_zoneSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastGoodScreen;
|
||||
}
|
||||
|
||||
void CServer::switchScreen(
|
||||
IScreen* screen, SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(screen != NULL);
|
||||
assert(m_running == true);
|
||||
assert(m_activeScreen != NULL);
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
SInt32 w, h;
|
||||
screen->getSize(&w, &h);
|
||||
assert(x >= 0 && y >= 0 && x < w && y < h);
|
||||
}
|
||||
#endif
|
||||
|
||||
TRACE(("switch %s to %s at %d,%d", m_activeScreen->getName().c_str(),
|
||||
screen->getName().c_str(), x, y));
|
||||
|
||||
// wrapping means leaving the active screen and entering it again.
|
||||
// since that's a waste of time we skip that and just warp the
|
||||
// mouse.
|
||||
if (m_activeScreen != screen) {
|
||||
// leave active screen
|
||||
m_activeScreen->leaveScreen();
|
||||
|
||||
// cut over
|
||||
m_activeScreen = screen;
|
||||
|
||||
// enter new screen
|
||||
m_activeScreen->enterScreen(x, y);
|
||||
}
|
||||
else {
|
||||
m_activeScreen->warpCursor(x, y);
|
||||
}
|
||||
|
||||
// record new position
|
||||
m_x = x;
|
||||
m_y = y;
|
||||
}
|
||||
|
||||
void CServer::newConnectionCB()
|
||||
{
|
||||
ISocket* socket = m_listenSocket->accept();
|
||||
TRACE(("accepted socket %p", socket));
|
||||
socket->setReadJob(new CServerSocketJob(this, &CServer::loginCB, socket));
|
||||
m_logins.insert(socket);
|
||||
}
|
||||
|
||||
void CServer::loginCB(ISocket* socket)
|
||||
{
|
||||
// FIXME -- no fixed size buffers
|
||||
UInt8 buffer[512];
|
||||
SInt32 n = socket->read(buffer, sizeof(buffer));
|
||||
if (n == -1) {
|
||||
TRACE(("socket %p disconnected", socket));
|
||||
goto fail;
|
||||
}
|
||||
TRACE(("read %d bytes from socket %p", n, socket));
|
||||
if (n <= 10) {
|
||||
TRACE(("socket %p: bogus %d byte message; hanging up", socket, n));
|
||||
goto fail;
|
||||
}
|
||||
if (n > 10) {
|
||||
if (::memcmp(buffer, "SYNERGY\000\001", 9) != 0) {
|
||||
TRACE(("socket %p: bad login", socket));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const SInt32 nameLen = static_cast<SInt32>(buffer[9]);
|
||||
if (nameLen < 1 || nameLen > 64) {
|
||||
TRACE(("socket %p: bad login name length %d", socket, nameLen));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (SInt32 i = 0; i < nameLen; ++i)
|
||||
if (!isalnum(buffer[10 + i])) {
|
||||
TRACE(("socket %p: bad login name", socket));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
CString name(reinterpret_cast<char*>(buffer + 10), nameLen);
|
||||
const ScreenMap::iterator index = m_map.find(name);
|
||||
if (index == m_map.end()) {
|
||||
TRACE(("socket %p: unknown screen %s", socket, name.c_str()));
|
||||
goto fail;
|
||||
}
|
||||
if (index->second.m_screen != NULL) {
|
||||
TRACE(("socket %p: screen %s already connected",
|
||||
socket, name.c_str()));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
TRACE(("socket %p: login %s", socket, name.c_str()));
|
||||
CScreenProxy* screen = new CScreenProxy(name, socket);
|
||||
m_logins.erase(socket);
|
||||
index->second.m_screen = screen;
|
||||
index->second.m_screen->open(false);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
m_logins.erase(socket);
|
||||
delete socket;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
#ifndef CSERVER_H
|
||||
#define CSERVER_H
|
||||
|
||||
#include "IServer.h"
|
||||
#include "BasicTypes.h"
|
||||
#include "CString.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
class CEvent;
|
||||
class CEventKey;
|
||||
class IScreen;
|
||||
class ISocket;
|
||||
|
||||
class CServer : public IServer {
|
||||
public:
|
||||
enum EDirection { kLeft, kRight, kTop, kBottom,
|
||||
kFirstDirection = kLeft, kLastDirection = kBottom };
|
||||
|
||||
CServer();
|
||||
virtual ~CServer();
|
||||
|
||||
// manipulators
|
||||
|
||||
// set the server's interface and port to listen for remote screens
|
||||
void setListenPort(const CString& hostname, UInt16 port);
|
||||
|
||||
// add local screen
|
||||
void addLocalScreen(IScreen*);
|
||||
|
||||
// add a remote screen
|
||||
void addRemoteScreen(const CString& name);
|
||||
|
||||
// remove a local or remote screen. neighbors on opposite sides
|
||||
// of this screen are made neighbors of each other.
|
||||
void removeScreen(const CString& name);
|
||||
|
||||
// connect/disconnect screen edges
|
||||
void connectEdge(const CString& src, EDirection srcSide,
|
||||
const CString& dst);
|
||||
void disconnectEdge(const CString& src, EDirection srcSide);
|
||||
|
||||
// accessors
|
||||
|
||||
|
||||
// IServer overrides
|
||||
virtual void run();
|
||||
virtual void onClipboardChanged(IScreen*);
|
||||
virtual void setActiveScreen(IScreen*);
|
||||
virtual IScreen* getActiveScreen() const;
|
||||
|
||||
protected:
|
||||
virtual void relayEvent(const CEvent* event);
|
||||
virtual bool onCommandKey(const CEventKey* keyEvent);
|
||||
virtual void onLocalMouseMove(SInt32 x, SInt32 y);
|
||||
virtual void onRemoteMouseMove(SInt32 dx, SInt32 dy);
|
||||
virtual bool isLockedToScreen() const;
|
||||
virtual void mapPosition(const IScreen* src, EDirection srcSide,
|
||||
const IScreen* dst, SInt32& x, SInt32& y) const;
|
||||
IScreen* getNeighbor(const IScreen* src, EDirection) const;
|
||||
IScreen* getNeighbor(const IScreen* src, EDirection srcSide,
|
||||
SInt32& x, SInt32& y) const;
|
||||
void switchScreen(IScreen* screen, SInt32 x, SInt32 y);
|
||||
|
||||
private:
|
||||
void addScreen(const CString&, IScreen*);
|
||||
void newConnectionCB();
|
||||
void loginCB(ISocket*);
|
||||
|
||||
struct ScreenCell {
|
||||
public:
|
||||
ScreenCell() : m_screen(NULL) { }
|
||||
public:
|
||||
IScreen* m_screen;
|
||||
CString m_neighbor[kLastDirection - kFirstDirection + 1];
|
||||
};
|
||||
|
||||
private:
|
||||
typedef std::map<CString, ScreenCell> ScreenMap;
|
||||
typedef std::set<ISocket*> SocketSet;
|
||||
|
||||
// main loop stuff
|
||||
bool m_running;
|
||||
bool m_done;
|
||||
|
||||
// screen tracking
|
||||
IScreen* m_localScreen;
|
||||
IScreen* m_activeScreen;
|
||||
SInt32 m_x, m_y;
|
||||
ScreenMap m_map;
|
||||
|
||||
// listen socket stuff
|
||||
CString m_listenHost;
|
||||
UInt16 m_listenPort;
|
||||
ISocket* m_listenSocket;
|
||||
|
||||
// login sockets
|
||||
SocketSet m_logins;
|
||||
|
||||
static const SInt32 s_zoneSize;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
#include "CSocket.h"
|
||||
#include "IJob.h"
|
||||
|
||||
//
|
||||
// CSocket
|
||||
//
|
||||
|
||||
CSocket::CSocket() : m_readJob(NULL), m_writeJob(NULL)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CSocket::~CSocket()
|
||||
{
|
||||
delete m_readJob;
|
||||
delete m_writeJob;
|
||||
}
|
||||
|
||||
void CSocket::setReadJob(IJob* adoptedJob)
|
||||
{
|
||||
delete m_readJob;
|
||||
m_readJob = adoptedJob;
|
||||
onJobChanged();
|
||||
}
|
||||
|
||||
void CSocket::setWriteJob(IJob* adoptedJob)
|
||||
{
|
||||
delete m_writeJob;
|
||||
m_writeJob = adoptedJob;
|
||||
onJobChanged();
|
||||
}
|
||||
|
||||
void CSocket::onJobChanged()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CSocket::runReadJob()
|
||||
{
|
||||
if (m_readJob)
|
||||
m_readJob->run();
|
||||
}
|
||||
|
||||
void CSocket::runWriteJob()
|
||||
{
|
||||
if (m_writeJob)
|
||||
m_writeJob->run();
|
||||
}
|
||||
|
||||
bool CSocket::hasReadJob() const
|
||||
{
|
||||
return (m_readJob != NULL);
|
||||
}
|
||||
|
||||
bool CSocket::hasWriteJob() const
|
||||
{
|
||||
return (m_writeJob != NULL);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef CSOCKET_H
|
||||
#define CSOCKET_H
|
||||
|
||||
#include "ISocket.h"
|
||||
|
||||
class IJob;
|
||||
|
||||
class CSocket : public ISocket {
|
||||
public:
|
||||
CSocket();
|
||||
virtual ~CSocket();
|
||||
|
||||
// ISocket overrides
|
||||
virtual void setReadJob(IJob* adoptedJob);
|
||||
virtual void setWriteJob(IJob* adoptedJob);
|
||||
virtual void connect(const CString& hostname, UInt16 port) = 0;
|
||||
virtual void listen(const CString& hostname, UInt16 port) = 0;
|
||||
virtual ISocket* accept() = 0;
|
||||
virtual SInt32 read(void* buffer, SInt32 numBytes) = 0;
|
||||
virtual void write(const void* buffer, SInt32 numBytes) = 0;
|
||||
|
||||
protected:
|
||||
// called when the read or write job is changed. default does nothing.
|
||||
virtual void onJobChanged();
|
||||
|
||||
// subclasses should call these at the appropriate time. different
|
||||
// platforms will arrange to detect these situations differently.
|
||||
// does nothing if the respective job is NULL.
|
||||
void runReadJob();
|
||||
void runWriteJob();
|
||||
|
||||
// return true iff the socket has a read job or a write job
|
||||
bool hasReadJob() const;
|
||||
bool hasWriteJob() const;
|
||||
|
||||
private:
|
||||
IJob* m_readJob;
|
||||
IJob* m_writeJob;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
#include "CSocketFactory.h"
|
||||
#include "BasicTypes.h"
|
||||
#include <assert.h>
|
||||
|
||||
//
|
||||
// CSocketFactory
|
||||
//
|
||||
|
||||
CSocketFactory* CSocketFactory::s_instance = NULL;
|
||||
|
||||
CSocketFactory::CSocketFactory()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CSocketFactory::~CSocketFactory()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CSocketFactory::setInstance(CSocketFactory* factory)
|
||||
{
|
||||
delete s_instance;
|
||||
s_instance = factory;
|
||||
}
|
||||
|
||||
CSocketFactory* CSocketFactory::getInstance()
|
||||
{
|
||||
assert(s_instance != NULL);
|
||||
return s_instance;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef CSOCKETFACTORY_H
|
||||
#define CSOCKETFACTORY_H
|
||||
|
||||
#define CSOCKETFACTORY CSocketFactory::getInstance()
|
||||
|
||||
class ISocket;
|
||||
|
||||
class CSocketFactory {
|
||||
public:
|
||||
CSocketFactory();
|
||||
virtual ~CSocketFactory();
|
||||
|
||||
// manipulators
|
||||
|
||||
static void setInstance(CSocketFactory*);
|
||||
|
||||
// accessors
|
||||
|
||||
// create a socket
|
||||
virtual ISocket* create() const = 0;
|
||||
|
||||
// get the global instance
|
||||
static CSocketFactory* getInstance();
|
||||
|
||||
private:
|
||||
static CSocketFactory* s_instance;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef CSTRING_H
|
||||
#define CSTRING_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifndef CSTRING_DEF_CTOR
|
||||
#define CSTRING_ALLOC1
|
||||
#define CSTRING_ALLOC2
|
||||
#define CSTRING_DEF_CTOR CString() : _Myt() { }
|
||||
#endif
|
||||
|
||||
// use to get appropriate type for string constants. it depends on
|
||||
// the internal representation type of CString.
|
||||
#define _CS(_x) _x
|
||||
|
||||
class CString : public std::string {
|
||||
public:
|
||||
typedef char _E;
|
||||
typedef _E CharT;
|
||||
typedef std::allocator<_E> _A;
|
||||
typedef std::string _Myt;
|
||||
typedef const_iterator _It;
|
||||
|
||||
// same constructors as base class
|
||||
CSTRING_DEF_CTOR
|
||||
CString(const _Myt& _X) : _Myt(_X) { }
|
||||
CString(const _Myt& _X, size_type _P, size_type _M CSTRING_ALLOC1) :
|
||||
_Myt(_X, _P, _M CSTRING_ALLOC2) { }
|
||||
CString(const _E *_S, size_type _N CSTRING_ALLOC1) :
|
||||
_Myt(_S, _N CSTRING_ALLOC2) { }
|
||||
CString(const _E *_S CSTRING_ALLOC1) :
|
||||
_Myt(_S CSTRING_ALLOC2) { }
|
||||
CString(size_type _N, _E _C CSTRING_ALLOC1) :
|
||||
_Myt(_N, _C CSTRING_ALLOC2) { }
|
||||
CString(_It _F, _It _L CSTRING_ALLOC1) :
|
||||
_Myt(_F, _L CSTRING_ALLOC2) { }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#include "CTrace.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// CTrace
|
||||
//
|
||||
|
||||
void CTrace::print(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(stderr, fmt, args);
|
||||
va_end(args);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef CTRACE_H
|
||||
#define CTRACE_H
|
||||
|
||||
class CTrace {
|
||||
public:
|
||||
static void print(const char* fmt, ...);
|
||||
};
|
||||
|
||||
#if defined(NDEBUG)
|
||||
|
||||
#define TRACE(_X)
|
||||
|
||||
#else // NDEBUG
|
||||
|
||||
#define TRACE(_X) CTrace::print ## _X
|
||||
|
||||
#endif // NDEBUG
|
||||
|
||||
#endif
|
|
@ -0,0 +1,141 @@
|
|||
#include "CUnixEventQueue.h"
|
||||
#include "IJob.h"
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//
|
||||
// CUnixEventQueue
|
||||
//
|
||||
|
||||
CUnixEventQueue::CUnixEventQueue()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CUnixEventQueue::~CUnixEventQueue()
|
||||
{
|
||||
// clean up lists
|
||||
clearList(m_readList);
|
||||
clearList(m_writeList);
|
||||
}
|
||||
|
||||
void CUnixEventQueue::addFileDesc(int fd,
|
||||
IJob* readJob, IJob* writeJob)
|
||||
{
|
||||
assert(fd != -1);
|
||||
assert(m_readList.count(fd) == 0 && m_writeList.count(fd) == 0);
|
||||
assert(readJob != writeJob || readJob == NULL);
|
||||
|
||||
if (readJob)
|
||||
m_readList[fd] = readJob;
|
||||
if (writeJob)
|
||||
m_writeList[fd] = writeJob;
|
||||
}
|
||||
|
||||
void CUnixEventQueue::removeFileDesc(int fd)
|
||||
{
|
||||
assert(fd != -1);
|
||||
|
||||
// remove from lists
|
||||
eraseList(m_readList, fd);
|
||||
eraseList(m_writeList, fd);
|
||||
}
|
||||
|
||||
void CUnixEventQueue::wait(double timeout)
|
||||
{
|
||||
// prepare sets
|
||||
fd_set fdRead, fdWrite;
|
||||
const int maxRead = prepList(m_readList, &fdRead);
|
||||
const int maxWrite = prepList(m_writeList, &fdWrite);
|
||||
|
||||
// compute the larger of maxRead and maxWrite
|
||||
const int fdMax = (maxRead > maxWrite) ? maxRead : maxWrite;
|
||||
if (fdMax == -1)
|
||||
return;
|
||||
|
||||
// prepare timeout
|
||||
struct timeval* pTimeout = NULL;
|
||||
struct timeval sTimeout;
|
||||
if (timeout >= 0.0) {
|
||||
sTimeout.tv_sec = static_cast<int>(timeout);
|
||||
sTimeout.tv_usec = static_cast<int>(1000000.0 *
|
||||
(timeout - sTimeout.tv_sec));
|
||||
pTimeout = &sTimeout;
|
||||
}
|
||||
|
||||
// wait
|
||||
const int n = ::select(fdMax + 1, &fdRead, &fdWrite, NULL, pTimeout);
|
||||
|
||||
// return on error or if nothing to do
|
||||
if (n <= 0)
|
||||
return;
|
||||
|
||||
// invoke jobs
|
||||
// note -- calling removeFileDesc() from a job is likely to crash the
|
||||
// program because we expect all jobs with active file descriptors to
|
||||
// persist for the duration of these loops.
|
||||
int fd;
|
||||
for (fd = 0; fd <= maxRead; ++fd)
|
||||
if (FD_ISSET(fd, &fdRead)) {
|
||||
assert(m_readList.count(fd) > 0);
|
||||
assert(m_readList[fd] != NULL);
|
||||
m_readList[fd]->run();
|
||||
}
|
||||
for (fd = 0; fd <= maxWrite; ++fd)
|
||||
if (FD_ISSET(fd, &fdWrite)) {
|
||||
assert(m_writeList.count(fd) > 0);
|
||||
assert(m_writeList[fd] != NULL);
|
||||
m_writeList[fd]->run();
|
||||
}
|
||||
}
|
||||
|
||||
void CUnixEventQueue::lock()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CUnixEventQueue::unlock()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CUnixEventQueue::signalNotEmpty()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CUnixEventQueue::eraseList(List& list, int fd) const
|
||||
{
|
||||
List::iterator index = list.find(fd);
|
||||
if (index != list.end()) {
|
||||
delete index->second;
|
||||
list.erase(index);
|
||||
}
|
||||
}
|
||||
|
||||
void CUnixEventQueue::clearList(List& list) const
|
||||
{
|
||||
for (List::const_iterator index = list.begin();
|
||||
index != list.end(); ++index)
|
||||
delete index->second;
|
||||
list.clear();
|
||||
}
|
||||
|
||||
int CUnixEventQueue::prepList(
|
||||
const List& list, void* vfdSet) const
|
||||
{
|
||||
fd_set* fdSet = reinterpret_cast<fd_set*>(vfdSet);
|
||||
FD_ZERO(fdSet);
|
||||
|
||||
int fdMax = -1;
|
||||
for (List::const_iterator index = list.begin();
|
||||
index != list.end(); ++index) {
|
||||
const int fd = index->first;
|
||||
FD_SET(fd, fdSet);
|
||||
if (fd > fdMax)
|
||||
fdMax = fd;
|
||||
}
|
||||
|
||||
return fdMax;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef CUNIXEVENTQUEUE_H
|
||||
#define CUNIXEVENTQUEUE_H
|
||||
|
||||
#include "CEventQueue.h"
|
||||
#include <map>
|
||||
|
||||
#undef CEQ
|
||||
#define CEQ ((CUnixEventQueue*)CEventQueue::getInstance())
|
||||
|
||||
class IJob;
|
||||
|
||||
class CUnixEventQueue : public CEventQueue {
|
||||
public:
|
||||
CUnixEventQueue();
|
||||
virtual ~CUnixEventQueue();
|
||||
|
||||
// manipulators
|
||||
|
||||
// add a file descriptor to wait on. if adoptedReadJob is not NULL
|
||||
// then it'll be called when the file descriptor is readable. if
|
||||
// adoptedWriteJob is not NULL then it will be called then the file
|
||||
// descriptor is writable. at least one job must not be NULL and
|
||||
// the jobs may not be the same. ownership of the jobs is assumed.
|
||||
// the file descriptor must not have already been added or, if it
|
||||
// was, it must have been removed.
|
||||
void addFileDesc(int fd,
|
||||
IJob* adoptedReadJob, IJob* adoptedWriteJob);
|
||||
|
||||
// remove a file descriptor from the list being waited on. the
|
||||
// associated jobs are destroyed. the file descriptor must have
|
||||
// been added and not since removed.
|
||||
void removeFileDesc(int fd);
|
||||
|
||||
// IEventQueue overrides
|
||||
virtual void wait(double timeout);
|
||||
|
||||
protected:
|
||||
// CEventQueue overrides
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual void signalNotEmpty();
|
||||
|
||||
private:
|
||||
typedef std::map<int, IJob*> List;
|
||||
void eraseList(List&, int fd) const;
|
||||
void clearList(List&) const;
|
||||
int prepList(const List&, void* fdSet) const;
|
||||
|
||||
private:
|
||||
List m_readList;
|
||||
List m_writeList;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,262 @@
|
|||
#include "CUnixTCPSocket.h"
|
||||
#include "CUnixEventQueue.h"
|
||||
#include "CString.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "XSocket.h"
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
extern int h_errno;
|
||||
|
||||
CUnixTCPSocket::CUnixTCPSocket() : m_fd(-1),
|
||||
m_state(kNone),
|
||||
m_addedJobs(false)
|
||||
{
|
||||
// create socket
|
||||
m_fd = ::socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_fd == -1)
|
||||
throw XSocketCreate(::strerror(errno));
|
||||
|
||||
// make it non-blocking
|
||||
int mode = ::fcntl(m_fd, F_GETFL, 0);
|
||||
if (mode == -1 || ::fcntl(m_fd, F_SETFL, mode | O_NONBLOCK) == -1) {
|
||||
::close(m_fd);
|
||||
throw XSocketCreate(::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd),
|
||||
m_state(kConnected),
|
||||
m_addedJobs(false)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
}
|
||||
|
||||
CUnixTCPSocket::~CUnixTCPSocket()
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
|
||||
// unhook events
|
||||
if (m_addedJobs)
|
||||
CEQ->removeFileDesc(m_fd);
|
||||
|
||||
// drain socket
|
||||
if (m_state == kConnected)
|
||||
::shutdown(m_fd, 0);
|
||||
|
||||
// close socket
|
||||
::close(m_fd);
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::onJobChanged()
|
||||
{
|
||||
// remove old jobs
|
||||
if (m_addedJobs) {
|
||||
CEQ->removeFileDesc(m_fd);
|
||||
m_addedJobs = false;
|
||||
}
|
||||
|
||||
// which jobs should we install?
|
||||
bool doRead = false;
|
||||
bool doWrite = false;
|
||||
switch (m_state) {
|
||||
case kNone:
|
||||
return;
|
||||
|
||||
case kConnecting:
|
||||
doWrite = true;
|
||||
break;
|
||||
|
||||
case kConnected:
|
||||
doRead = hasReadJob();
|
||||
doWrite = hasWriteJob();
|
||||
break;
|
||||
|
||||
case kListening:
|
||||
doRead = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// make jobs
|
||||
IJob* readJob = doRead ? new TMethodJob<CUnixTCPSocket>(this,
|
||||
&CUnixTCPSocket::readCB) : NULL;
|
||||
IJob* writeJob = doWrite ? new TMethodJob<CUnixTCPSocket>(this,
|
||||
&CUnixTCPSocket::writeCB) : NULL;
|
||||
|
||||
// install jobs
|
||||
CEQ->addFileDesc(m_fd, readJob, writeJob);
|
||||
m_addedJobs = true;
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::readCB()
|
||||
{
|
||||
runReadJob();
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::writeCB()
|
||||
{
|
||||
if (m_state == kConnecting) {
|
||||
// now connected. start watching for reads.
|
||||
m_state = kConnected;
|
||||
onJobChanged();
|
||||
}
|
||||
runWriteJob();
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::connect(
|
||||
const CString& hostname, UInt16 port)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
assert(m_state == kNone);
|
||||
|
||||
// hostname to address
|
||||
struct hostent* hent = ::gethostbyname(hostname.c_str());
|
||||
if (hent == NULL)
|
||||
throw XSocketName(::hstrerror(h_errno));
|
||||
|
||||
// construct address
|
||||
struct sockaddr_in addr;
|
||||
assert(hent->h_addrtype == AF_INET);
|
||||
assert(hent->h_length == sizeof(addr.sin_addr));
|
||||
addr.sin_family = hent->h_addrtype;
|
||||
addr.sin_port = htons(port);
|
||||
::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length);
|
||||
|
||||
// start connecting
|
||||
if (::connect(m_fd, reinterpret_cast<struct sockaddr*>(&addr),
|
||||
sizeof(addr)) == -1) {
|
||||
if (errno != EINPROGRESS)
|
||||
throw XSocketConnect(::strerror(errno));
|
||||
m_state = kConnecting;
|
||||
}
|
||||
else {
|
||||
m_state = kConnected;
|
||||
runWriteJob();
|
||||
}
|
||||
onJobChanged();
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::listen(
|
||||
const CString& hostname, UInt16 port)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
assert(m_state == kNone);
|
||||
assert(port != 0);
|
||||
|
||||
// construct address
|
||||
struct sockaddr_in addr;
|
||||
if (!hostname.empty()) {
|
||||
// hostname to address
|
||||
struct hostent* hent = ::gethostbyname(hostname.c_str());
|
||||
if (hent == NULL)
|
||||
throw XSocketName(::hstrerror(h_errno));
|
||||
|
||||
// fill in address
|
||||
assert(hent->h_addrtype == AF_INET);
|
||||
assert(hent->h_length == sizeof(addr.sin_addr));
|
||||
::memcpy(&addr.sin_addr, hent->h_addr_list[0], hent->h_length);
|
||||
}
|
||||
else {
|
||||
// all addresses
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
// bind to address
|
||||
if (::bind(m_fd, reinterpret_cast<struct sockaddr*>(&addr),
|
||||
sizeof(addr)) == -1)
|
||||
throw XSocketListen(::strerror(errno));
|
||||
|
||||
// start listening
|
||||
if (::listen(m_fd, 3) == -1)
|
||||
throw XSocketListen(::strerror(errno));
|
||||
m_state = kListening;
|
||||
onJobChanged();
|
||||
}
|
||||
|
||||
ISocket* CUnixTCPSocket::accept()
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
assert(m_state == kListening);
|
||||
|
||||
for (;;) {
|
||||
// wait for connection
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(m_fd, &fdset);
|
||||
::select(m_fd + 1, &fdset, NULL, NULL, NULL);
|
||||
|
||||
// accept connection
|
||||
struct sockaddr addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
int fd = ::accept(m_fd, &addr, &addrlen);
|
||||
if (fd == -1)
|
||||
if (errno == EAGAIN)
|
||||
continue;
|
||||
else
|
||||
throw XSocketAccept(::strerror(errno));
|
||||
|
||||
// return new socket object
|
||||
return new CUnixTCPSocket(fd);
|
||||
}
|
||||
}
|
||||
|
||||
SInt32 CUnixTCPSocket::read(void* buffer, SInt32 numBytes)
|
||||
{
|
||||
assert(m_fd != -1);
|
||||
assert(m_state == kConnected);
|
||||
|
||||
const ssize_t n = ::read(m_fd, buffer, numBytes);
|
||||
if (n == -1) {
|
||||
// check for no data to read
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
return 0;
|
||||
|
||||
// error
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check for socket closed
|
||||
if (n == 0)
|
||||
return -1;
|
||||
|
||||
// return num bytes read
|
||||
return n;
|
||||
}
|
||||
|
||||
void CUnixTCPSocket::write(
|
||||
const void* buffer, SInt32 numBytes)
|
||||
{
|
||||
const char* ptr = static_cast<const char*>(buffer);
|
||||
|
||||
while (numBytes > 0) {
|
||||
// write more data
|
||||
const ssize_t n = ::write(m_fd, ptr, numBytes);
|
||||
|
||||
// check for errors
|
||||
if (n == -1) {
|
||||
// wait if can't write data then try again
|
||||
if (errno == EAGAIN || errno == EINTR) {
|
||||
fd_set fdset;
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(m_fd, &fdset);
|
||||
::select(m_fd + 1, NULL, &fdset, NULL, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
// error
|
||||
throw XSocketWrite(::strerror(errno));
|
||||
}
|
||||
|
||||
// account for written data
|
||||
ptr += n;
|
||||
numBytes -= n;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef CUNIXTCPSOCKET_H
|
||||
#define CUNIXTCPSOCKET_H
|
||||
|
||||
#include "CSocket.h"
|
||||
#include "CSocketFactory.h"
|
||||
|
||||
class CUnixTCPSocket : public CSocket {
|
||||
public:
|
||||
CUnixTCPSocket();
|
||||
virtual ~CUnixTCPSocket();
|
||||
|
||||
// ISocket overrides
|
||||
virtual void connect(const CString& hostname, UInt16 port);
|
||||
virtual void listen(const CString& hostname, UInt16 port);
|
||||
virtual ISocket* accept();
|
||||
virtual SInt32 read(void* buffer, SInt32 numBytes);
|
||||
virtual void write(const void* buffer, SInt32 numBytes);
|
||||
|
||||
protected:
|
||||
// CSocket overrides
|
||||
virtual void onJobChanged();
|
||||
|
||||
private:
|
||||
CUnixTCPSocket(int);
|
||||
|
||||
// callbacks for read/write events
|
||||
void readCB();
|
||||
void writeCB();
|
||||
|
||||
private:
|
||||
enum EState { kNone, kConnecting, kConnected, kListening };
|
||||
int m_fd;
|
||||
EState m_state;
|
||||
bool m_addedJobs;
|
||||
};
|
||||
|
||||
class CUnixTCPSocketFactory : public CSocketFactory {
|
||||
public:
|
||||
CUnixTCPSocketFactory() { }
|
||||
virtual ~CUnixTCPSocketFactory() { }
|
||||
|
||||
// CSocketFactory overrides
|
||||
virtual ISocket* create() const
|
||||
{ return new CUnixTCPSocket; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,34 @@
|
|||
#include "CUnixXScreen.h"
|
||||
#include "CUnixEventQueue.h"
|
||||
#include "TMethodJob.h"
|
||||
#include <X11/X.h>
|
||||
|
||||
//
|
||||
// CUnixXScreen
|
||||
//
|
||||
|
||||
CUnixXScreen::CUnixXScreen(const CString& name) :
|
||||
CXScreen(name)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CUnixXScreen::~CUnixXScreen()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void CUnixXScreen::onOpen(bool)
|
||||
{
|
||||
// register our X event handler
|
||||
CEQ->addFileDesc(ConnectionNumber(getDisplay()),
|
||||
new TMethodJob<CUnixXScreen>(this,
|
||||
&CUnixXScreen::onEvents), NULL);
|
||||
|
||||
}
|
||||
|
||||
void CUnixXScreen::onClose()
|
||||
{
|
||||
// unregister the X event handler
|
||||
CEQ->removeFileDesc(ConnectionNumber(getDisplay()));
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CUNIXXSCREEN_H
|
||||
#define CUNIXXSCREEN_H
|
||||
|
||||
#include "CXScreen.h"
|
||||
|
||||
class CUnixXScreen : public CXScreen {
|
||||
public:
|
||||
CUnixXScreen(const CString& name);
|
||||
virtual ~CUnixXScreen();
|
||||
|
||||
protected:
|
||||
virtual void onOpen(bool isPrimary);
|
||||
virtual void onClose();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,571 @@
|
|||
#include "CXScreen.h"
|
||||
#include "CEvent.h"
|
||||
#include "CEventQueue.h"
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/extensions/XTest.h>
|
||||
|
||||
//
|
||||
// CXScreen
|
||||
//
|
||||
class XClientOpen { }; // FIXME
|
||||
|
||||
CXScreen::CXScreen(const CString& name) :
|
||||
m_name(name),
|
||||
m_display(NULL),
|
||||
m_primary(false),
|
||||
m_w(0), m_h(0),
|
||||
m_window(None),
|
||||
m_active(false)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CXScreen::~CXScreen()
|
||||
{
|
||||
assert(m_display == NULL);
|
||||
}
|
||||
|
||||
void CXScreen::open(bool isPrimary)
|
||||
{
|
||||
assert(m_display == NULL);
|
||||
|
||||
m_primary = isPrimary;
|
||||
|
||||
bool opened = false;
|
||||
try {
|
||||
// open the display
|
||||
m_display = ::XOpenDisplay(NULL); // FIXME -- allow non-default
|
||||
if (m_display == NULL)
|
||||
throw XClientOpen();
|
||||
|
||||
// hook up event handling
|
||||
onOpen(m_primary);
|
||||
opened = true;
|
||||
|
||||
// get default screen
|
||||
m_screen = DefaultScreen(m_display);
|
||||
Screen* screen = ScreenOfDisplay(m_display, m_screen);
|
||||
|
||||
// get screen size
|
||||
m_w = WidthOfScreen(screen);
|
||||
m_h = HeightOfScreen(screen);
|
||||
|
||||
// type specific operations
|
||||
if (m_primary)
|
||||
openPrimary();
|
||||
else
|
||||
openSecondary();
|
||||
}
|
||||
catch (...) {
|
||||
if (opened)
|
||||
onClose();
|
||||
|
||||
if (m_display != NULL) {
|
||||
::XCloseDisplay(m_display);
|
||||
m_display = NULL;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CXScreen::close()
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
// type specific operations
|
||||
if (m_primary)
|
||||
closePrimary();
|
||||
else
|
||||
closeSecondary();
|
||||
|
||||
// unhook event handling
|
||||
onClose();
|
||||
|
||||
// close the display
|
||||
::XCloseDisplay(m_display);
|
||||
m_display = NULL;
|
||||
}
|
||||
|
||||
void CXScreen::enterScreen(SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
if (m_primary)
|
||||
enterScreenPrimary(x, y);
|
||||
else
|
||||
enterScreenSecondary(x, y);
|
||||
}
|
||||
|
||||
void CXScreen::leaveScreen()
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
if (m_primary)
|
||||
leaveScreenPrimary();
|
||||
else
|
||||
leaveScreenSecondary();
|
||||
}
|
||||
|
||||
void CXScreen::warpCursor(SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
// warp the mouse
|
||||
Window root = RootWindow(m_display, m_screen);
|
||||
::XWarpPointer(m_display, None, root, 0, 0, 0, 0, x, y);
|
||||
::XSync(m_display, False);
|
||||
|
||||
// discard mouse events since we just added one we don't want
|
||||
XEvent xevent;
|
||||
while (::XCheckWindowEvent(m_display, m_window,
|
||||
PointerMotionMask, &xevent))
|
||||
; // do nothing
|
||||
}
|
||||
|
||||
void CXScreen::setClipboard(
|
||||
const IClipboard* clipboard)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
if (m_primary)
|
||||
setClipboardPrimary(clipboard);
|
||||
else
|
||||
setClipboardSecondary(clipboard);
|
||||
}
|
||||
|
||||
void CXScreen::onScreenSaver(bool show)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
if (m_primary)
|
||||
onScreenSaverPrimary(show);
|
||||
else
|
||||
onScreenSaverSecondary(show);
|
||||
}
|
||||
|
||||
void CXScreen::onKeyDown(KeyID)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onKeyRepeat(KeyID, SInt32)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onKeyUp(KeyID)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onKeyToggle(KeyToggleMask)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onMouseDown(ButtonID)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onMouseUp(ButtonID)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onMouseMove(SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime);
|
||||
}
|
||||
|
||||
void CXScreen::onMouseWheel(SInt32)
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onClipboardChanged()
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(m_primary == false);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
CString CXScreen::getName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void CXScreen::getSize(
|
||||
SInt32* width, SInt32* height) const
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
assert(width != NULL && height != NULL);
|
||||
|
||||
*width = m_w;
|
||||
*height = m_h;
|
||||
}
|
||||
|
||||
void CXScreen::getClipboard(
|
||||
IClipboard* /*clipboard*/) const
|
||||
{
|
||||
assert(m_display != NULL);
|
||||
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::openPrimary()
|
||||
{
|
||||
// get the root window
|
||||
Window root = RootWindow(m_display, m_screen);
|
||||
|
||||
// create the grab window. this window is used to capture user
|
||||
// input when the user is focussed on another client. don't let
|
||||
// the window manager mess with it.
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = PointerMotionMask |// PointerMotionHintMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
KeymapStateMask;
|
||||
attr.do_not_propagate_mask = 0;
|
||||
attr.override_redirect = True;
|
||||
attr.cursor = None;
|
||||
m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0,
|
||||
InputOnly, CopyFromParent,
|
||||
CWDontPropagate | CWEventMask |
|
||||
CWOverrideRedirect | CWCursor,
|
||||
&attr);
|
||||
|
||||
// start watching for events on other windows
|
||||
selectEvents(root);
|
||||
}
|
||||
|
||||
void CXScreen::closePrimary()
|
||||
{
|
||||
assert(m_window != None);
|
||||
|
||||
// destroy window
|
||||
::XDestroyWindow(m_display, m_window);
|
||||
m_window = None;
|
||||
}
|
||||
|
||||
void CXScreen::enterScreenPrimary(SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(m_window != None);
|
||||
assert(m_active == true);
|
||||
|
||||
// warp to requested location
|
||||
::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y);
|
||||
|
||||
// unmap the grab window. this also ungrabs the mouse and keyboard.
|
||||
::XUnmapWindow(m_display, m_window);
|
||||
|
||||
// remove all input events for grab window
|
||||
XEvent event;
|
||||
while (::XCheckWindowEvent(m_display, m_window,
|
||||
PointerMotionMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
KeymapStateMask,
|
||||
&event))
|
||||
; // do nothing
|
||||
|
||||
// not active anymore
|
||||
m_active = false;
|
||||
}
|
||||
|
||||
void CXScreen::leaveScreenPrimary()
|
||||
{
|
||||
assert(m_window != None);
|
||||
assert(m_active == false);
|
||||
|
||||
// raise and show the input window
|
||||
::XMapRaised(m_display, m_window);
|
||||
|
||||
// grab the mouse and keyboard. keep trying until we get them.
|
||||
// if we can't grab one after grabbing the other then ungrab
|
||||
// and wait before retrying.
|
||||
int result;
|
||||
do {
|
||||
// mouse first
|
||||
do {
|
||||
result = ::XGrabPointer(m_display, m_window, True, 0,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
m_window, None, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess)
|
||||
::sleep(1);
|
||||
} while (result != GrabSuccess);
|
||||
|
||||
// now the keyboard
|
||||
result = ::XGrabKeyboard(m_display, m_window, True,
|
||||
GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess) {
|
||||
::XUngrabPointer(m_display, CurrentTime);
|
||||
::sleep(1);
|
||||
}
|
||||
} while (result != GrabSuccess);
|
||||
|
||||
// move the mouse to the center of grab window
|
||||
warpCursor(m_w >> 1, m_h >> 1);
|
||||
|
||||
// local client now active
|
||||
m_active = true;
|
||||
}
|
||||
|
||||
void CXScreen::setClipboardPrimary(
|
||||
const IClipboard* /*clipboard*/)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onScreenSaverPrimary(bool /*show*/)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::openSecondary()
|
||||
{
|
||||
// verify the availability of the XTest extension
|
||||
int majorOpcode, firstEvent, firstError;
|
||||
if (!::XQueryExtension(m_display, XTestExtensionName,
|
||||
&majorOpcode, &firstEvent, &firstError))
|
||||
throw XClientOpen();
|
||||
|
||||
// become impervious to server grabs
|
||||
XTestGrabControl(m_display, True);
|
||||
}
|
||||
|
||||
void CXScreen::closeSecondary()
|
||||
{
|
||||
// no longer impervious to server grabs
|
||||
XTestGrabControl(m_display, False);
|
||||
}
|
||||
|
||||
void CXScreen::enterScreenSecondary(
|
||||
SInt32 x, SInt32 y)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::leaveScreenSecondary()
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::setClipboardSecondary(
|
||||
const IClipboard* /*clipboard*/)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
void CXScreen::onScreenSaverSecondary(bool /*show*/)
|
||||
{
|
||||
// FIXME
|
||||
}
|
||||
|
||||
Display* CXScreen::getDisplay() const
|
||||
{
|
||||
return m_display;
|
||||
}
|
||||
|
||||
void CXScreen::onEvents()
|
||||
{
|
||||
if (m_primary)
|
||||
onPrimaryEvents();
|
||||
else
|
||||
onSecondaryEvents();
|
||||
}
|
||||
|
||||
void CXScreen::selectEvents(Window w) const
|
||||
{
|
||||
// we want to track the mouse everywhere on the display. to achieve
|
||||
// that we select PointerMotionMask on every window. we also select
|
||||
// SubstructureNotifyMask in order to get CreateNotify events so we
|
||||
// select events on new windows too.
|
||||
|
||||
// we don't want to adjust our grab window
|
||||
if (w == m_window)
|
||||
return;
|
||||
|
||||
// select events of interest
|
||||
::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask);
|
||||
|
||||
// recurse on child windows
|
||||
Window rw, pw, *cw;
|
||||
unsigned int nc;
|
||||
if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
|
||||
for (unsigned int i = 0; i < nc; ++i)
|
||||
selectEvents(cw[i]);
|
||||
::XFree(cw);
|
||||
}
|
||||
}
|
||||
|
||||
KeyID CXScreen::mapKey(unsigned int keycode) const
|
||||
{
|
||||
return keycode;
|
||||
}
|
||||
|
||||
ButtonID CXScreen::mapButton(unsigned int button) const
|
||||
{
|
||||
switch (button) {
|
||||
case 1:
|
||||
return kButtonLeft;
|
||||
|
||||
case 2:
|
||||
return kButtonMiddle;
|
||||
|
||||
case 3:
|
||||
return kButtonRight;
|
||||
}
|
||||
|
||||
return kButtonNone;
|
||||
}
|
||||
|
||||
void CXScreen::onPrimaryEvents()
|
||||
{
|
||||
while (XPending(m_display) > 0) {
|
||||
XEvent xevent;
|
||||
XNextEvent(m_display, &xevent);
|
||||
|
||||
switch (xevent.type) {
|
||||
case KeyPress: {
|
||||
const KeyID key = mapKey(xevent.xkey.keycode);
|
||||
if (key != kKeyNone) {
|
||||
CEvent event;
|
||||
event.m_key.m_type = CEventBase::kKeyDown;
|
||||
event.m_key.m_key = key;
|
||||
event.m_key.m_count = 0;
|
||||
CEQ->push(&event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME -- simulate key repeat. X sends press/release for
|
||||
// repeat. must detect auto repeat and use kKeyRepeat.
|
||||
case KeyRelease: {
|
||||
const KeyID key = mapKey(xevent.xkey.keycode);
|
||||
if (key != kKeyNone) {
|
||||
CEvent event;
|
||||
event.m_key.m_type = CEventBase::kKeyUp;
|
||||
event.m_key.m_key = key;
|
||||
event.m_key.m_count = 0;
|
||||
CEQ->push(&event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ButtonPress: {
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
CEvent event;
|
||||
event.m_mouse.m_type = CEventBase::kMouseDown;
|
||||
event.m_mouse.m_button = button;
|
||||
event.m_mouse.m_x = 0;
|
||||
event.m_mouse.m_y = 0;
|
||||
CEQ->push(&event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ButtonRelease: {
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
CEvent event;
|
||||
event.m_mouse.m_type = CEventBase::kMouseUp;
|
||||
event.m_mouse.m_button = button;
|
||||
event.m_mouse.m_x = 0;
|
||||
event.m_mouse.m_y = 0;
|
||||
CEQ->push(&event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionNotify: {
|
||||
CEvent event;
|
||||
event.m_mouse.m_type = CEventBase::kMouseMove;
|
||||
event.m_mouse.m_button = kButtonNone;
|
||||
if (!m_active) {
|
||||
event.m_mouse.m_x = xevent.xmotion.x_root;
|
||||
event.m_mouse.m_y = xevent.xmotion.y_root;
|
||||
}
|
||||
else {
|
||||
// FIXME -- slurp up all remaining motion events?
|
||||
// probably not since key strokes may go to wrong place.
|
||||
|
||||
// get mouse deltas
|
||||
Window root, window;
|
||||
int xRoot, yRoot, xWindow, yWindow;
|
||||
unsigned int mask;
|
||||
if (!::XQueryPointer(m_display, m_window, &root, &window,
|
||||
&xRoot, &yRoot, &xWindow, &yWindow, &mask))
|
||||
break;
|
||||
event.m_mouse.m_x = xRoot - (m_w >> 1);
|
||||
event.m_mouse.m_y = yRoot - (m_h >> 1);
|
||||
|
||||
// warp mouse back to center
|
||||
warpCursor(m_w >> 1, m_h >> 1);
|
||||
}
|
||||
CEQ->push(&event);
|
||||
break;
|
||||
}
|
||||
|
||||
case CreateNotify:
|
||||
// select events on new window
|
||||
if (m_primary)
|
||||
selectEvents(xevent.xcreatewindow.window);
|
||||
break;
|
||||
|
||||
/*
|
||||
case SelectionClear:
|
||||
target->XXX(xevent.xselectionclear.);
|
||||
break;
|
||||
|
||||
case SelectionNotify:
|
||||
target->XXX(xevent.xselection.);
|
||||
break;
|
||||
|
||||
case SelectionRequest:
|
||||
target->XXX(xevent.xselectionrequest.);
|
||||
break;
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CXScreen::onSecondaryEvents()
|
||||
{
|
||||
while (XPending(m_display) > 0) {
|
||||
XEvent xevent;
|
||||
XNextEvent(m_display, &xevent);
|
||||
// FIXME
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef CXSCREEN_H
|
||||
#define CXSCREEN_H
|
||||
|
||||
#include "IScreen.h"
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
class CXScreen : public IScreen {
|
||||
public:
|
||||
CXScreen(const CString& name);
|
||||
virtual ~CXScreen();
|
||||
|
||||
// IScreen overrides
|
||||
virtual void open(bool isPrimary);
|
||||
virtual void close();
|
||||
virtual void enterScreen(SInt32 x, SInt32 y);
|
||||
virtual void leaveScreen();
|
||||
virtual void warpCursor(SInt32 x, SInt32 y);
|
||||
virtual void setClipboard(const IClipboard*);
|
||||
virtual void onScreenSaver(bool);
|
||||
virtual void onKeyDown(KeyID);
|
||||
virtual void onKeyRepeat(KeyID, SInt32);
|
||||
virtual void onKeyUp(KeyID);
|
||||
virtual void onKeyToggle(KeyToggleMask);
|
||||
virtual void onMouseDown(ButtonID);
|
||||
virtual void onMouseUp(ButtonID);
|
||||
virtual void onMouseMove(SInt32, SInt32);
|
||||
virtual void onMouseWheel(SInt32);
|
||||
virtual void onClipboardChanged();
|
||||
virtual CString getName() const;
|
||||
virtual void getSize(SInt32* width, SInt32* height) const;
|
||||
virtual void getClipboard(IClipboard*) const;
|
||||
|
||||
protected:
|
||||
// primary screen implementations
|
||||
virtual void openPrimary();
|
||||
virtual void closePrimary();
|
||||
virtual void enterScreenPrimary(SInt32 x, SInt32 y);
|
||||
virtual void leaveScreenPrimary();
|
||||
virtual void setClipboardPrimary(const IClipboard*);
|
||||
virtual void onScreenSaverPrimary(bool);
|
||||
|
||||
// secondary screen implementations
|
||||
virtual void openSecondary();
|
||||
virtual void closeSecondary();
|
||||
virtual void enterScreenSecondary(SInt32 x, SInt32 y);
|
||||
virtual void leaveScreenSecondary();
|
||||
virtual void setClipboardSecondary(const IClipboard*);
|
||||
virtual void onScreenSaverSecondary(bool);
|
||||
|
||||
// get the display
|
||||
Display* getDisplay() const;
|
||||
|
||||
// process X events from the display
|
||||
void onEvents();
|
||||
|
||||
// called by open() and close(). override to hook up and unhook the
|
||||
// display connection to the event queue. call onEvents() when events
|
||||
// are available.
|
||||
virtual void onOpen(bool isPrimary) = 0;
|
||||
virtual void onClose() = 0;
|
||||
|
||||
private:
|
||||
void selectEvents(Window) const;
|
||||
KeyID mapKey(unsigned int keycode) const;
|
||||
ButtonID mapButton(unsigned int button) const;
|
||||
void onPrimaryEvents();
|
||||
void onSecondaryEvents();
|
||||
|
||||
private:
|
||||
CString m_name;
|
||||
Display* m_display;
|
||||
int m_screen;
|
||||
bool m_primary;
|
||||
SInt32 m_w, m_h;
|
||||
|
||||
// stuff for primary screens
|
||||
Window m_window;
|
||||
bool m_active;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef ICLIENT_H
|
||||
#define ICLIENT_H
|
||||
|
||||
class CString;
|
||||
|
||||
class IClient {
|
||||
public:
|
||||
IClient() { }
|
||||
virtual ~IClient() { }
|
||||
|
||||
// manipulators
|
||||
|
||||
// connect to server and begin processing events
|
||||
virtual void run(const CString& hostname) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef IEVENTQUEUE_H
|
||||
#define IEVENTQUEUE_H
|
||||
|
||||
#define CEQ (IEventQueue::getInstance())
|
||||
|
||||
class CEvent;
|
||||
|
||||
class IEventQueue {
|
||||
public:
|
||||
IEventQueue();
|
||||
virtual ~IEventQueue();
|
||||
|
||||
// note -- all of the methods in an IEventQueue subclass for a
|
||||
// platform must be thread safe if it will be used by multiple
|
||||
// threads simultaneously on that platform.
|
||||
|
||||
// manipulators
|
||||
|
||||
// wait up to timeout seconds for the queue to become not empty.
|
||||
// as a side effect this can do the insertion of events. if
|
||||
// timeout < 0.0 then wait indefinitely. it's possible for
|
||||
// wait() to return prematurely so always call isEmpty() to
|
||||
// see if there are any events.
|
||||
virtual void wait(double timeout) = 0;
|
||||
|
||||
// reads and removes the next event on the queue. waits indefinitely
|
||||
// for an event if the queue is empty.
|
||||
virtual void pop(CEvent*) = 0;
|
||||
|
||||
// push an event onto the queue
|
||||
virtual void push(const CEvent*) = 0;
|
||||
|
||||
// returns true if the queue is empty and wait() would block
|
||||
virtual bool isEmpty() = 0;
|
||||
|
||||
// accessors
|
||||
|
||||
// get the singleton event queue
|
||||
static IEventQueue* getInstance();
|
||||
|
||||
private:
|
||||
static IEventQueue* s_instance;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef IJOB_H
|
||||
#define IJOB_H
|
||||
|
||||
class IJob {
|
||||
public:
|
||||
IJob() { }
|
||||
virtual ~IJob() { }
|
||||
|
||||
// manipulators
|
||||
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,132 @@
|
|||
#ifndef ISCREEN_H
|
||||
#define ISCREEN_H
|
||||
|
||||
/*
|
||||
* IScreen -- interface for display screens
|
||||
*
|
||||
* a screen encapsulates input and output devices, typically a mouse
|
||||
* and keyboard for input and a graphical display for output. one
|
||||
* screen is designated as the primary screen. only input from the
|
||||
* primary screen's input devices is used. other screens are secondary
|
||||
* screens and they simulate input from their input devices but ignore
|
||||
* any actual input. a screen can be either a primary or a secondary
|
||||
* but not both at the same time. most methods behave differently
|
||||
* depending on the screen type.
|
||||
*/
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "KeyTypes.h"
|
||||
#include "MouseTypes.h"
|
||||
#include "CString.h"
|
||||
|
||||
class IClipboard;
|
||||
|
||||
class IScreen {
|
||||
public:
|
||||
IScreen() { }
|
||||
virtual ~IScreen() { }
|
||||
|
||||
// manipulators
|
||||
|
||||
// open/close screen. these are where the client should do
|
||||
// initialization and cleanup of the system's screen. if isPrimary
|
||||
// is true then this screen will be used (exclusively) as the
|
||||
// primary screen, otherwise it will be used (exclusively) as a
|
||||
// secondary screen.
|
||||
//
|
||||
// primary:
|
||||
// open(): open the screen and begin reporting input events to
|
||||
// the event queue. input events should be reported no matter
|
||||
// where on the screen they occur but the screen should not
|
||||
// interfere with the normal dispatching of events. the screen
|
||||
// should detect when the screen saver is activated. if it can't
|
||||
// do that it should disable the screen saver and start it itself
|
||||
// after the appropriate duration of no input.
|
||||
//
|
||||
// secondary:
|
||||
// open(): open the screen, hide the cursor and disable the
|
||||
// screen saver. then wait for an enterScreen() or close(),
|
||||
// reporting the following events: FIXME.
|
||||
virtual void open(bool isPrimary) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
// enter/leave screen
|
||||
//
|
||||
// primary:
|
||||
// enterScreen(): the user has navigated back to the primary
|
||||
// screen. warp the cursor to the given coordinates, unhide the
|
||||
// cursor and ungrab the input devices. the screen must also
|
||||
// detect and report (enqueue) input events. for the primary
|
||||
// screen, enterScreen() is only called after a leaveScreen().
|
||||
// leaveScreen(): the user has navigated off the primary screen.
|
||||
// hide the cursor and grab exclusive access to the input devices.
|
||||
// input events must be reported.
|
||||
//
|
||||
// secondary:
|
||||
// enterScreen(): the user has navigated to this secondary
|
||||
// screen. warp the cursor to the given coordinates and show it.
|
||||
// prepare to simulate input events.
|
||||
// leaveScreen(): the user has navigated off this secondary
|
||||
// screen. clean up input event simulation. hide the cursor.
|
||||
virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
||||
virtual void leaveScreen() = 0;
|
||||
|
||||
// warp the cursor to the given position
|
||||
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
||||
|
||||
//
|
||||
// clipboard operations
|
||||
//
|
||||
|
||||
// set the screen's clipboard contents. this is usually called
|
||||
// soon after an enterScreen().
|
||||
virtual void setClipboard(const IClipboard*) = 0;
|
||||
|
||||
//
|
||||
// screen saver operations
|
||||
//
|
||||
|
||||
// show or hide the screen saver
|
||||
virtual void onScreenSaver(bool show) = 0;
|
||||
|
||||
//
|
||||
// input simulation
|
||||
//
|
||||
// these methods must simulate the appropriate input event.
|
||||
// these methods may only called on secondary screens.
|
||||
//
|
||||
|
||||
// keyboard input
|
||||
// onKeyToggle() sets the keyboard toggle key states (e.g. num lock).
|
||||
virtual void onKeyDown(KeyID) = 0;
|
||||
virtual void onKeyRepeat(KeyID, SInt32 count) = 0;
|
||||
virtual void onKeyUp(KeyID) = 0;
|
||||
virtual void onKeyToggle(KeyToggleMask) = 0;
|
||||
|
||||
// mouse input
|
||||
virtual void onMouseDown(ButtonID) = 0;
|
||||
virtual void onMouseUp(ButtonID) = 0;
|
||||
virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
||||
virtual void onMouseWheel(SInt32 delta) = 0;
|
||||
|
||||
// clipboard input
|
||||
// FIXME -- do we need this?
|
||||
virtual void onClipboardChanged() = 0;
|
||||
|
||||
// accessors
|
||||
|
||||
// get the screen's name. all screens must have a name unique on
|
||||
// the server they connect to. the hostname is usually an
|
||||
// appropriate name.
|
||||
virtual CString getName() const = 0;
|
||||
|
||||
// get the size of the screen
|
||||
virtual void getSize(SInt32* width, SInt32* height) const = 0;
|
||||
|
||||
// clipboard operations
|
||||
|
||||
// get the screen's clipboard contents
|
||||
virtual void getClipboard(IClipboard*) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef ISERVER_H
|
||||
#define ISERVER_H
|
||||
|
||||
class IScreen;
|
||||
|
||||
class IServer {
|
||||
public:
|
||||
IServer() { }
|
||||
virtual ~IServer() { }
|
||||
|
||||
// manipulators
|
||||
|
||||
// run the server until terminated
|
||||
virtual void run() = 0;
|
||||
|
||||
// clipboard operations
|
||||
virtual void onClipboardChanged(IScreen*) = 0;
|
||||
|
||||
// enter the given screen, leaving the previous screen. the cursor
|
||||
// should be warped to the center of the screen.
|
||||
virtual void setActiveScreen(IScreen*) = 0;
|
||||
|
||||
// accessors
|
||||
|
||||
// get the screen that was last entered
|
||||
virtual IScreen* getActiveScreen() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef ISOCKET_H
|
||||
#define ISOCKET_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
|
||||
class IJob;
|
||||
class CString;
|
||||
|
||||
class ISocket {
|
||||
public:
|
||||
// d'tor closes the socket
|
||||
ISocket() { }
|
||||
virtual ~ISocket() { }
|
||||
|
||||
// manipulators
|
||||
|
||||
// set the job to invoke when the socket is readable or writable.
|
||||
// a socket that has connected after a call to connect() becomes
|
||||
// writable. a socket that is ready to accept a connection after
|
||||
// a call to listen() becomes readable. the socket returned by
|
||||
// accept() does not have any jobs assigned to it.
|
||||
virtual void setReadJob(IJob* adoptedJob) = 0;
|
||||
virtual void setWriteJob(IJob* adoptedJob) = 0;
|
||||
|
||||
// open/close. connect() begins connecting to the given host but
|
||||
// doesn't wait for the connection to complete. listen() begins
|
||||
// listening on the given interface and port; if hostname is
|
||||
// empty then listen on all interfaces. accept() waits for a
|
||||
// connection on the listening interface and returns a new
|
||||
// socket for the connection.
|
||||
virtual void connect(const CString& hostname, UInt16 port) = 0;
|
||||
virtual void listen(const CString& hostname, UInt16 port) = 0;
|
||||
virtual ISocket* accept() = 0;
|
||||
|
||||
// read data from socket. returns without waiting if not enough
|
||||
// data is available. returns the number of bytes actually read,
|
||||
// which is zero if there were no bytes to read and -1 if the
|
||||
// remote end of the socket has disconnected.
|
||||
virtual SInt32 read(void* buffer, SInt32 numBytes) = 0;
|
||||
|
||||
// write data to socket. waits until all data has been written.
|
||||
virtual void write(const void* buffer, SInt32 numBytes) = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef KEYTYPES_H
|
||||
#define KEYTYPES_H
|
||||
|
||||
// type to hold a key identifier
|
||||
typedef UInt32 KeyID;
|
||||
|
||||
// type to hold bitmask of keys that have toggle states
|
||||
typedef UInt16 KeyToggleMask;
|
||||
|
||||
// toggle key bitmasks
|
||||
static const UInt32 KeyToggleShiftLock = 0x0001;
|
||||
static const UInt32 KeyToggleNumLock = 0x0002;
|
||||
static const UInt32 KeyToggleScrollLock = 0x0004;
|
||||
|
||||
// key codes
|
||||
static const KeyID kKeyNone = 0;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,103 @@
|
|||
#
|
||||
# empty define, used to terminate lists
|
||||
#
|
||||
NULL =
|
||||
|
||||
#
|
||||
# build tools
|
||||
#
|
||||
CXX = /usr/bin/g++
|
||||
LD = /usr/bin/ld
|
||||
MKDIR = /bin/mkdir
|
||||
RM = /bin/rm -f
|
||||
RMR = /bin/rm -rf
|
||||
|
||||
#
|
||||
# compiler options
|
||||
#
|
||||
CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT)
|
||||
LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS)
|
||||
GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS)
|
||||
|
||||
GCXXDEFS = -D_POSIX_SOURCE
|
||||
GCXXINCS = -I$(DEPTH)/include -I/usr/include -I/usr/X11R6/include
|
||||
GCXXOPTS = -Wall -W -fexceptions -fno-rtti
|
||||
CXXOPTIMIZER = -g
|
||||
#CXXOPTIMIZER = -O2 -DNDEBUG
|
||||
|
||||
#
|
||||
# linker options
|
||||
#
|
||||
LDFLAGS = $(LDOPTS) $(LDLIBS)
|
||||
LDOPTS = $(LLDOPTS) $(GLDOPTS)
|
||||
LDLIBS = $(LLDLIBS) $(GLDLIBS)
|
||||
|
||||
GLDLIBS = -L/usr/X11R6/lib -lX11 -lXext -lXtst
|
||||
GLDOPTS =
|
||||
|
||||
#
|
||||
# dependency generation stuff
|
||||
#
|
||||
MKDEP = $(DEPTH)/tools/depconv
|
||||
MKDEPOPT = -MD
|
||||
MKDEPPRE =
|
||||
MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d
|
||||
MKDEPFILE = Makedepend
|
||||
|
||||
#
|
||||
# Convenience file list macros:
|
||||
#
|
||||
SOURCES = $(CXXFILES)
|
||||
OBJECTS = $(CXXFILES:.cpp=.o)
|
||||
|
||||
#
|
||||
# stuff to clean
|
||||
#
|
||||
DIRT = $(_FORCE) $(LDIRT) $(GDIRT)
|
||||
GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE)
|
||||
|
||||
#
|
||||
# always unsatisfied target
|
||||
#
|
||||
_FORCE = $(COMMONPREF)_force
|
||||
|
||||
#
|
||||
#
|
||||
# common rules
|
||||
#
|
||||
#
|
||||
|
||||
#
|
||||
# default target. makefiles must define a target named `targets'.
|
||||
#
|
||||
default: targets
|
||||
|
||||
#
|
||||
# always unsatisfied target
|
||||
#
|
||||
$(_FORCE):
|
||||
|
||||
#
|
||||
# cleaners
|
||||
#
|
||||
COMMONTARGETS = clean clobber
|
||||
$(COMMONPREF)clean: $(_FORCE)
|
||||
$(RM) $(DIRT)
|
||||
|
||||
$(COMMONPREF)clobber: clean $(_FORCE)
|
||||
$(RM) $(TARGETS)
|
||||
|
||||
#
|
||||
# implicit target rules
|
||||
#
|
||||
.SUFFIXES: .cpp .o
|
||||
|
||||
.cpp.o:
|
||||
$(MKDEPPRE)
|
||||
$(CXX) $(CXXFLAGS) -c $<
|
||||
$(MKDEPPOST)
|
||||
|
||||
#
|
||||
# load dependencies
|
||||
#
|
||||
sinclude $(MKDEPFILE)
|
|
@ -0,0 +1,38 @@
|
|||
DEPTH=.
|
||||
include Make-linux
|
||||
|
||||
#
|
||||
# target files
|
||||
#
|
||||
TARGETS = main
|
||||
|
||||
#
|
||||
# source files
|
||||
#
|
||||
CXXFILES = \
|
||||
XBase.cpp \
|
||||
CTrace.cpp \
|
||||
CEventQueue.cpp \
|
||||
CSocket.cpp \
|
||||
CMessageSocket.cpp \
|
||||
CSocketFactory.cpp \
|
||||
CServer.cpp \
|
||||
CClient.cpp \
|
||||
CScreenProxy.cpp \
|
||||
CXScreen.cpp \
|
||||
CUnixXScreen.cpp \
|
||||
CUnixTCPSocket.cpp \
|
||||
CUnixEventQueue.cpp \
|
||||
main.cpp \
|
||||
$(NULL)
|
||||
|
||||
#
|
||||
# libraries we depend on
|
||||
#
|
||||
DEPLIBS = \
|
||||
$(NULL)
|
||||
|
||||
targets: $(TARGETS)
|
||||
|
||||
main: $(OBJECTS) $(DEPLIBS)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJECTS) $(LDFLAGS)
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef MOUSETYPES_H
|
||||
#define MOUSETYPES_H
|
||||
|
||||
// type to hold mouse button identifier
|
||||
typedef UInt8 ButtonID;
|
||||
|
||||
// mouse button identifiers
|
||||
static const ButtonID kButtonNone = 0;
|
||||
static const ButtonID kButtonLeft = 1;
|
||||
static const ButtonID kButtonRight = 2;
|
||||
static const ButtonID kButtonMiddle = 3;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef TMETHODJOB_H
|
||||
#define TMETHODJOB_H
|
||||
|
||||
#include "IJob.h"
|
||||
|
||||
template <class T>
|
||||
class TMethodJob : public IJob {
|
||||
public:
|
||||
typedef void (T::*Method)();
|
||||
|
||||
TMethodJob(T* object, Method method) :
|
||||
m_object(object), m_method(method) { }
|
||||
virtual ~TMethodJob() { }
|
||||
|
||||
// IJob overrides
|
||||
virtual void run() { (m_object->*m_method)(); }
|
||||
|
||||
private:
|
||||
T* m_object;
|
||||
Method m_method;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,40 @@
|
|||
#include "XBase.h"
|
||||
|
||||
// win32 wants a const char* argument to std::exception c'tor
|
||||
#if CONFIG_PLATFORM_WIN32
|
||||
#define STDEXCEPTARG ""
|
||||
#endif
|
||||
|
||||
// default to no argument
|
||||
#ifndef STDEXCEPTARG
|
||||
#define STDEXCEPTARG
|
||||
#endif
|
||||
|
||||
//
|
||||
// XBase
|
||||
//
|
||||
|
||||
XBase::XBase() : exception(STDEXCEPTARG)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
XBase::~XBase()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const char* XBase::what() const
|
||||
{
|
||||
return getType();
|
||||
}
|
||||
|
||||
const char* XBase::getType() const
|
||||
{
|
||||
return "XBase.h";
|
||||
}
|
||||
|
||||
CString XBase::format(const CString& fmt) const
|
||||
{
|
||||
return fmt;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef XBASE_H
|
||||
#define XBASE_H
|
||||
|
||||
#include "CString.h"
|
||||
#include <exception>
|
||||
|
||||
class XBase : public std::exception {
|
||||
public:
|
||||
XBase();
|
||||
virtual ~XBase();
|
||||
|
||||
// accessors
|
||||
|
||||
// return the name of the exception type
|
||||
virtual const char* getType() const;
|
||||
|
||||
// format and return formatString by replacing positional
|
||||
// arguments (%1, %2, etc.). default returns formatString
|
||||
// unchanged. subclasses should document what positional
|
||||
// arguments they replace.
|
||||
virtual CString format(const CString& formatString) const;
|
||||
|
||||
// std::exception overrides
|
||||
virtual const char* what() const;
|
||||
};
|
||||
|
||||
#define XNAME(_n) \
|
||||
public: \
|
||||
virtual const char* getType() const { return #_n; }
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef XSOCKET_H
|
||||
#define XSOCKET_H
|
||||
|
||||
#include "XBase.h"
|
||||
|
||||
class XSocket : public XBase {
|
||||
public:
|
||||
// accessors
|
||||
|
||||
const char* getMessage() const { return m_msg; }
|
||||
|
||||
protected:
|
||||
XSocket(const char* msg) : m_msg(msg) { }
|
||||
|
||||
private:
|
||||
const char* m_msg;
|
||||
};
|
||||
|
||||
#define XSOCKETDEF(_n) \
|
||||
class _n : public XSocket { \
|
||||
public: \
|
||||
_n(const char* msg) : XSocket(msg) { } \
|
||||
XNAME(_n) \
|
||||
};
|
||||
|
||||
XSOCKETDEF(XSocketCreate)
|
||||
XSOCKETDEF(XSocketName)
|
||||
XSOCKETDEF(XSocketConnect)
|
||||
XSOCKETDEF(XSocketListen)
|
||||
XSOCKETDEF(XSocketAccept)
|
||||
XSOCKETDEF(XSocketWrite)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,114 @@
|
|||
#include <stdio.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "CServer.h"
|
||||
#include "CClient.h"
|
||||
#include "CUnixTCPSocket.h"
|
||||
#include "CUnixEventQueue.h"
|
||||
#include "CUnixXScreen.h"
|
||||
|
||||
/*
|
||||
static void selectMotion(Display* dpy, Window w)
|
||||
{
|
||||
// select events
|
||||
XSelectInput(dpy, w, PointerMotionMask | SubstructureNotifyMask);
|
||||
|
||||
// recurse on child windows
|
||||
Window rw, pw, *cw;
|
||||
unsigned int nc;
|
||||
if (XQueryTree(dpy, w, &rw, &pw, &cw, &nc)) {
|
||||
for (unsigned int i = 0; i < nc; ++i)
|
||||
selectMotion(dpy, cw[i]);
|
||||
XFree(cw);
|
||||
}
|
||||
}
|
||||
|
||||
static void trackMouse(Display* dpy)
|
||||
{
|
||||
// note -- this doesn't track the mouse when it's grabbed. that's
|
||||
// okay for synergy because we don't want to cross screens then.
|
||||
selectMotion(dpy, DefaultRootWindow(dpy));
|
||||
while (true) {
|
||||
XEvent event;
|
||||
XNextEvent(dpy, &event);
|
||||
switch (event.type) {
|
||||
case MotionNotify:
|
||||
fprintf(stderr, "mouse: %d,%d\n", event.xmotion.x_root, event.xmotion.y_root);
|
||||
break;
|
||||
|
||||
case CreateNotify:
|
||||
selectMotion(dpy, event.xcreatewindow.window);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void checkLEDs(Display* dpy)
|
||||
{
|
||||
XKeyboardState values;
|
||||
XGetKeyboardControl(dpy, &values);
|
||||
|
||||
fprintf(stderr, "led (%08x): ", (unsigned int)values.led_mask);
|
||||
for (int i = 0; i < 32; ++i)
|
||||
fprintf(stderr, "%c", (values.led_mask & (1 << i)) ? 'O' : '.');
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
XKeyboardControl ctrl;
|
||||
for (int i = 0; i < 32; i += 2) {
|
||||
ctrl.led = i + 1;
|
||||
ctrl.led_mode = LedModeOff;
|
||||
XChangeKeyboardControl(dpy, KBLed | KBLedMode, &ctrl);
|
||||
XSync(dpy, False);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
/*
|
||||
printf("Hello world\n");
|
||||
|
||||
Display* dpy = XOpenDisplay(NULL);
|
||||
|
||||
checkLEDs(dpy);
|
||||
trackMouse(dpy);
|
||||
|
||||
XCloseDisplay(dpy);
|
||||
*/
|
||||
|
||||
// install socket factory
|
||||
CSocketFactory::setInstance(new CUnixTCPSocketFactory);
|
||||
|
||||
// create event queue
|
||||
CUnixEventQueue eventQueue;
|
||||
|
||||
if (argc <= 1) {
|
||||
// create server
|
||||
CServer server;
|
||||
|
||||
// create clients
|
||||
CUnixXScreen localScreen("audrey2");
|
||||
|
||||
// register clients
|
||||
server.addLocalScreen(&localScreen);
|
||||
server.addRemoteScreen("remote1");
|
||||
|
||||
// hook up edges
|
||||
server.connectEdge("audrey2", CServer::kLeft, "remote1");
|
||||
server.connectEdge("audrey2", CServer::kTop, "audrey2");
|
||||
server.connectEdge("audrey2", CServer::kBottom, "audrey2");
|
||||
server.connectEdge("remote1", CServer::kLeft, "audrey2");
|
||||
|
||||
// do it
|
||||
server.run();
|
||||
}
|
||||
else {
|
||||
// create client
|
||||
CUnixXScreen screen("remote1");
|
||||
CClient client(&screen);
|
||||
client.run(argv[1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# add dependency info from files on command line to $depfile
|
||||
|
||||
depfile="Makedepends"
|
||||
|
||||
dependencies=""
|
||||
targets="^$"
|
||||
|
||||
# tmp directory
|
||||
if test -z "$TMP"; then TMP=/tmp; fi
|
||||
|
||||
while [ -n "$*" ]; do
|
||||
case "$1" in
|
||||
-f)
|
||||
depfile=$2
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "usage: $0 [-f <makefile>] <dependency-file> ..."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
done
|
||||
|
||||
# collect all dependencies
|
||||
while [ -n "$*" ]; do
|
||||
line=`cat $1 | sed -e 's/\\\\//g' | sed -e 's/ \/[^ ]*//g'`
|
||||
target=`echo $line | sed -e 's/^\([^:]*\):.*/\1/'`
|
||||
targets="$targets|^$target:"
|
||||
dependencies="$dependencies$line\n"
|
||||
shift
|
||||
done
|
||||
|
||||
# add new dependencies to $depfile
|
||||
if [ -n "$targets" ]; then
|
||||
if [ -r $depfile ]; then
|
||||
(egrep -v $targets $depfile; echo -e -n $dependencies) > $TMP/dep$$
|
||||
if [ $? -eq 0 ]; then mv $TMP/dep$$ $depfile; fi
|
||||
else
|
||||
echo -e -n $dependencies > $depfile
|
||||
fi
|
||||
fi
|
Loading…
Reference in New Issue