Started over.
This commit is contained in:
parent
27ead1f713
commit
ff81f708e2
33
BasicTypes.h
33
BasicTypes.h
|
@ -1,33 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
||||||
|
|
293
CClient.cpp
293
CClient.cpp
|
@ -1,293 +0,0 @@
|
||||||
#include "CClient.h"
|
|
||||||
#include "CString.h"
|
|
||||||
#include "TMethodJob.h"
|
|
||||||
#include "IScreen.h"
|
|
||||||
#include "ISocket.h"
|
|
||||||
#include "CMessageSocket.h"
|
|
||||||
#include "CSocketFactory.h"
|
|
||||||
#include "IEventQueue.h"
|
|
||||||
#include "CEvent.h"
|
|
||||||
#include "CTrace.h"
|
|
||||||
#include <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]) ));
|
|
||||||
const KeyModifierMask m = static_cast<KeyModifierMask>(
|
|
||||||
(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 down: %d 0x%08x", k, m));
|
|
||||||
m_screen->onKeyDown(k, m);
|
|
||||||
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 KeyModifierMask m = static_cast<KeyModifierMask>(
|
|
||||||
(static_cast<UInt32>(buf[5]) << 24) +
|
|
||||||
(static_cast<UInt32>(buf[6]) << 16) +
|
|
||||||
(static_cast<UInt32>(buf[7]) << 8) +
|
|
||||||
(static_cast<UInt32>(buf[8]) ));
|
|
||||||
const SInt32 n = static_cast<SInt32>(
|
|
||||||
(static_cast<UInt32>(buf[9]) << 24) +
|
|
||||||
(static_cast<UInt32>(buf[10]) << 16) +
|
|
||||||
(static_cast<UInt32>(buf[11]) << 8) +
|
|
||||||
(static_cast<UInt32>(buf[12]) ));
|
|
||||||
TRACE((" key repeat: %d 0x%08x x%d", k, m, n));
|
|
||||||
m_screen->onKeyRepeat(k, m, n);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case '\011': {
|
|
||||||
const KeyID k = static_cast<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 KeyModifierMask m = static_cast<KeyModifierMask>(
|
|
||||||
(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 up: %d 0x%08x", k, m));
|
|
||||||
m_screen->onKeyUp(k, 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));
|
|
||||||
}
|
|
28
CClient.h
28
CClient.h
|
@ -1,28 +0,0 @@
|
||||||
#ifndef CCLIENT_H
|
|
||||||
#define CCLIENT_H
|
|
||||||
|
|
||||||
#include "IClient.h"
|
|
||||||
|
|
||||||
class IScreen;
|
|
||||||
class ISocket;
|
|
||||||
|
|
||||||
class CClient : public IClient {
|
|
||||||
public:
|
|
||||||
CClient(IScreen* screen);
|
|
||||||
virtual ~CClient();
|
|
||||||
|
|
||||||
// IClient overrides
|
|
||||||
virtual void run(const CString& hostname);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onConnect();
|
|
||||||
void onRead();
|
|
||||||
|
|
||||||
void sendScreenSize();
|
|
||||||
|
|
||||||
private:
|
|
||||||
IScreen* m_screen;
|
|
||||||
ISocket* m_socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
59
CEvent.h
59
CEvent.h
|
@ -1,59 +0,0 @@
|
||||||
#ifndef CEVENT_H
|
|
||||||
#define CEVENT_H
|
|
||||||
|
|
||||||
#include "BasicTypes.h"
|
|
||||||
#include "KeyTypes.h"
|
|
||||||
#include "MouseTypes.h"
|
|
||||||
|
|
||||||
class ISocket;
|
|
||||||
|
|
||||||
class CEventBase {
|
|
||||||
public:
|
|
||||||
enum EType {
|
|
||||||
kNull,
|
|
||||||
kKeyDown,
|
|
||||||
kKeyRepeat,
|
|
||||||
kKeyUp,
|
|
||||||
kMouseDown,
|
|
||||||
kMouseUp,
|
|
||||||
kMouseMove,
|
|
||||||
kMouseWheel,
|
|
||||||
kScreenSize
|
|
||||||
};
|
|
||||||
|
|
||||||
EType m_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEventKey : public CEventBase {
|
|
||||||
public:
|
|
||||||
KeyID m_key;
|
|
||||||
KeyModifierMask m_mask;
|
|
||||||
SInt32 m_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEventMouse : public CEventBase {
|
|
||||||
public:
|
|
||||||
ButtonID m_button;
|
|
||||||
SInt32 m_x; // or wheel delta
|
|
||||||
SInt32 m_y;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEventSize : public CEventBase {
|
|
||||||
public:
|
|
||||||
SInt32 m_w;
|
|
||||||
SInt32 m_h;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CEvent {
|
|
||||||
public:
|
|
||||||
union {
|
|
||||||
public:
|
|
||||||
CEventBase m_any;
|
|
||||||
CEventKey m_key;
|
|
||||||
CEventMouse m_mouse;
|
|
||||||
CEventSize m_size;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include "CEventQueue.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// IEventQueue
|
|
||||||
//
|
|
||||||
|
|
||||||
IEventQueue* IEventQueue::s_instance = NULL;
|
|
||||||
|
|
||||||
IEventQueue::IEventQueue()
|
|
||||||
{
|
|
||||||
assert(s_instance == NULL);
|
|
||||||
s_instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEventQueue::~IEventQueue()
|
|
||||||
{
|
|
||||||
s_instance = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
IEventQueue* IEventQueue::getInstance()
|
|
||||||
{
|
|
||||||
return s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// CEventQueue
|
|
||||||
//
|
|
||||||
|
|
||||||
CEventQueue::CEventQueue()
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
CEventQueue::~CEventQueue()
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEventQueue::pop(CEvent* event)
|
|
||||||
{
|
|
||||||
assert(event != NULL);
|
|
||||||
|
|
||||||
// wait for an event
|
|
||||||
while (isEmpty())
|
|
||||||
wait(-1.0);
|
|
||||||
|
|
||||||
// lock the queue, extract an event, then unlock
|
|
||||||
lock();
|
|
||||||
*event = m_queue.front();
|
|
||||||
m_queue.pop_front();
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEventQueue::push(const CEvent* event)
|
|
||||||
{
|
|
||||||
// put the event at the end of the queue and signal that the queue
|
|
||||||
// is not empty
|
|
||||||
lock();
|
|
||||||
m_queue.push_back(*event);
|
|
||||||
signalNotEmpty();
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CEventQueue::isEmpty()
|
|
||||||
{
|
|
||||||
lock();
|
|
||||||
bool e = m_queue.empty();
|
|
||||||
unlock();
|
|
||||||
|
|
||||||
// if queue is empty then poll to see if more stuff is ready to go
|
|
||||||
// on the queue and check again if the queue is empty.
|
|
||||||
if (e) {
|
|
||||||
wait(0.0);
|
|
||||||
lock();
|
|
||||||
e = m_queue.empty();
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#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
|
|
|
@ -1,153 +0,0 @@
|
||||||
#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[0]) << 8) +
|
|
||||||
(static_cast<UInt32>(m_buffer[1]) ));
|
|
||||||
TRACE((" next message size: %d", m_msgSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CMessageSocket::write(const void* buffer, SInt32 n)
|
|
||||||
{
|
|
||||||
// FIXME -- no fixed size buffers
|
|
||||||
char tmp[512];
|
|
||||||
assert(n < (SInt32)sizeof(tmp) - 2);
|
|
||||||
::memcpy(tmp + 2, buffer, n);
|
|
||||||
n += 2;
|
|
||||||
tmp[0] = static_cast<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();
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
#ifndef CMESSAGESOCKET_H
|
|
||||||
#define CMESSAGESOCKET_H
|
|
||||||
|
|
||||||
#include "CSocket.h"
|
|
||||||
|
|
||||||
class CMessageSocket : public CSocket {
|
|
||||||
public:
|
|
||||||
CMessageSocket(ISocket* adoptedSocket);
|
|
||||||
virtual ~CMessageSocket();
|
|
||||||
|
|
||||||
// ISocket overrides
|
|
||||||
// connect(), listen(), and accept() may not be called.
|
|
||||||
virtual void setWriteJob(IJob* adoptedJob);
|
|
||||||
virtual void connect(const CString& hostname, UInt16 port);
|
|
||||||
virtual void listen(const CString& hostname, UInt16 port);
|
|
||||||
virtual ISocket* accept();
|
|
||||||
virtual SInt32 read(void* buffer, SInt32 numBytes);
|
|
||||||
virtual void write(const void* buffer, SInt32 numBytes);
|
|
||||||
|
|
||||||
private:
|
|
||||||
SInt32 doRead();
|
|
||||||
virtual void readJobCB();
|
|
||||||
virtual void writeJobCB();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ISocket* m_socket;
|
|
||||||
UInt8* m_buffer;
|
|
||||||
SInt32 m_size;
|
|
||||||
SInt32 m_capacity;
|
|
||||||
SInt32 m_msgSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
20
CProtocol.h
20
CProtocol.h
|
@ -1,20 +0,0 @@
|
||||||
#ifndef CPROTOCOL_H
|
|
||||||
#define CPROTOCOL_H
|
|
||||||
|
|
||||||
#include "BasicTypes.h"
|
|
||||||
|
|
||||||
class CProtocol {
|
|
||||||
public:
|
|
||||||
CProtocol();
|
|
||||||
virtual ~CProtocol();
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
void ReadMessage(ISocket*, CMessage&) const;
|
|
||||||
void WriteMessage(ISocket*, const CMessage&) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
238
CScreenProxy.cpp
238
CScreenProxy.cpp
|
@ -1,238 +0,0 @@
|
||||||
#include "CScreenProxy.h"
|
|
||||||
#include "ISocket.h"
|
|
||||||
#include "CMessageSocket.h"
|
|
||||||
#include "TMethodJob.h"
|
|
||||||
#include "CTrace.h"
|
|
||||||
//
|
|
||||||
// CScreenProxy
|
|
||||||
//
|
|
||||||
|
|
||||||
CScreenProxy::CScreenProxy(const CString& name, ISocket* socket) :
|
|
||||||
m_name(name),
|
|
||||||
m_socket(socket),
|
|
||||||
m_w(0), m_h(0)
|
|
||||||
{
|
|
||||||
assert(!m_name.empty());
|
|
||||||
assert(m_socket != NULL);
|
|
||||||
|
|
||||||
m_socket = new CMessageSocket(m_socket);
|
|
||||||
m_socket->setReadJob(new TMethodJob<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, KeyModifierMask m)
|
|
||||||
{
|
|
||||||
char buf[9];
|
|
||||||
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);
|
|
||||||
buf[5] = static_cast<char>((m >> 24) & 0xff);
|
|
||||||
buf[6] = static_cast<char>((m >> 16) & 0xff);
|
|
||||||
buf[7] = static_cast<char>((m >> 8) & 0xff);
|
|
||||||
buf[8] = static_cast<char>(m & 0xff);
|
|
||||||
m_socket->write(buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScreenProxy::onKeyRepeat(
|
|
||||||
KeyID k, KeyModifierMask m, SInt32 n)
|
|
||||||
{
|
|
||||||
char buf[13];
|
|
||||||
memcpy(buf, "\010", 1);
|
|
||||||
buf[1] = static_cast<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>((m >> 24) & 0xff);
|
|
||||||
buf[6] = static_cast<char>((m >> 16) & 0xff);
|
|
||||||
buf[7] = static_cast<char>((m >> 8) & 0xff);
|
|
||||||
buf[8] = static_cast<char>(m & 0xff);
|
|
||||||
buf[9] = static_cast<char>((n >> 24) & 0xff);
|
|
||||||
buf[10] = static_cast<char>((n >> 16) & 0xff);
|
|
||||||
buf[11] = static_cast<char>((n >> 8) & 0xff);
|
|
||||||
buf[12] = static_cast<char>(n & 0xff);
|
|
||||||
m_socket->write(buf, sizeof(buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CScreenProxy::onKeyUp(KeyID k, KeyModifierMask m)
|
|
||||||
{
|
|
||||||
char buf[9];
|
|
||||||
memcpy(buf, "\011", 1);
|
|
||||||
buf[1] = static_cast<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>((m >> 24) & 0xff);
|
|
||||||
buf[6] = static_cast<char>((m >> 16) & 0xff);
|
|
||||||
buf[7] = static_cast<char>((m >> 8) & 0xff);
|
|
||||||
buf[8] = 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
#ifndef CSCREENPROXY_H
|
|
||||||
#define CSCREENPROXY_H
|
|
||||||
|
|
||||||
#include "IScreen.h"
|
|
||||||
|
|
||||||
class ISocket;
|
|
||||||
|
|
||||||
class CScreenProxy : public IScreen {
|
|
||||||
public:
|
|
||||||
CScreenProxy(const CString& name, ISocket*);
|
|
||||||
virtual ~CScreenProxy();
|
|
||||||
|
|
||||||
// IScreen overrides
|
|
||||||
virtual void open(bool);
|
|
||||||
virtual void close();
|
|
||||||
virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute);
|
|
||||||
virtual void leaveScreen();
|
|
||||||
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute);
|
|
||||||
virtual void setClipboard(const IClipboard*);
|
|
||||||
virtual void onScreenSaver(bool show);
|
|
||||||
virtual void onKeyDown(KeyID, KeyModifierMask);
|
|
||||||
virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count);
|
|
||||||
virtual void onKeyUp(KeyID, KeyModifierMask);
|
|
||||||
virtual void onMouseDown(ButtonID);
|
|
||||||
virtual void onMouseUp(ButtonID);
|
|
||||||
virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute);
|
|
||||||
virtual void onMouseWheel(SInt32 delta);
|
|
||||||
virtual void onClipboardChanged();
|
|
||||||
virtual CString getName() const;
|
|
||||||
virtual void getSize(SInt32* width, SInt32* height) const;
|
|
||||||
virtual void getClipboard(IClipboard*) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onRead();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CString m_name;
|
|
||||||
ISocket* m_socket;
|
|
||||||
SInt32 m_w, m_h;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
812
CServer.cpp
812
CServer.cpp
|
@ -1,812 +0,0 @@
|
||||||
#include "CServer.h"
|
|
||||||
#include "CEvent.h"
|
|
||||||
#include "IEventQueue.h"
|
|
||||||
#include "IScreen.h"
|
|
||||||
#include "CScreenProxy.h"
|
|
||||||
#include "ISocket.h"
|
|
||||||
#include "CSocketFactory.h"
|
|
||||||
#include "CMessageSocket.h"
|
|
||||||
#include "TMethodJob.h"
|
|
||||||
#include "CTrace.h"
|
|
||||||
#include <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, event->m_key.m_mask);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kKeyRepeat:
|
|
||||||
m_activeScreen->onKeyRepeat(event->m_key.m_key,
|
|
||||||
event->m_key.m_mask, event->m_key.m_count);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kKeyUp:
|
|
||||||
m_activeScreen->onKeyUp(event->m_key.m_key, event->m_key.m_mask);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kMouseDown:
|
|
||||||
m_activeScreen->onMouseDown(event->m_mouse.m_button);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kMouseUp:
|
|
||||||
m_activeScreen->onMouseUp(event->m_mouse.m_button);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kMouseWheel:
|
|
||||||
m_activeScreen->onMouseWheel(event->m_mouse.m_x);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CEventBase::kMouseMove:
|
|
||||||
assert(0 && "kMouseMove relayed");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0 && "invalid event relayed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CServer::onCommandKey(const CEventKey* /*keyEvent*/)
|
|
||||||
{
|
|
||||||
// FIXME -- strip out command keys (e.g. lock to screen, warp, etc.)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CServer::onLocalMouseMove(SInt32 x, SInt32 y)
|
|
||||||
{
|
|
||||||
assert(m_activeScreen == m_localScreen);
|
|
||||||
|
|
||||||
// ignore if locked to screen
|
|
||||||
if (isLockedToScreen())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// get local screen's size
|
|
||||||
SInt32 w, h;
|
|
||||||
m_activeScreen->getSize(&w, &h);
|
|
||||||
|
|
||||||
// see if we should change screens
|
|
||||||
EDirection dir;
|
|
||||||
if (x < s_zoneSize) {
|
|
||||||
x -= s_zoneSize;
|
|
||||||
dir = kLeft;
|
|
||||||
}
|
|
||||||
else if (x >= w - s_zoneSize) {
|
|
||||||
x += s_zoneSize;
|
|
||||||
dir = kRight;
|
|
||||||
}
|
|
||||||
else if (y < s_zoneSize) {
|
|
||||||
y -= s_zoneSize;
|
|
||||||
dir = kTop;
|
|
||||||
}
|
|
||||||
else if (y >= h - s_zoneSize) {
|
|
||||||
y += s_zoneSize;
|
|
||||||
dir = kBottom;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// still on local screen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TRACE(("leave %s on %s", m_activeScreen->getName().c_str(), s_dirName[dir]));
|
|
||||||
|
|
||||||
// get new screen. if no screen in that direction then ignore move.
|
|
||||||
IScreen* newScreen = getNeighbor(m_activeScreen, dir, x, y);
|
|
||||||
if (newScreen == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// remap position to account for resolution differences between screens
|
|
||||||
mapPosition(m_activeScreen, dir, newScreen, x, y);
|
|
||||||
|
|
||||||
// switch screen
|
|
||||||
switchScreen(newScreen, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CServer::onRemoteMouseMove(SInt32 dx, SInt32 dy)
|
|
||||||
{
|
|
||||||
assert(m_activeScreen != NULL);
|
|
||||||
assert(m_activeScreen != m_localScreen);
|
|
||||||
|
|
||||||
// put mouse back in center of local screen's grab area
|
|
||||||
// XXX m_localScreen->warpToCenter();
|
|
||||||
|
|
||||||
// save old position
|
|
||||||
const SInt32 xOld = m_x;
|
|
||||||
const SInt32 yOld = m_y;
|
|
||||||
|
|
||||||
// accumulate mouse position
|
|
||||||
m_x += dx;
|
|
||||||
m_y += dy;
|
|
||||||
|
|
||||||
// get active screen's size
|
|
||||||
SInt32 w, h;
|
|
||||||
m_activeScreen->getSize(&w, &h);
|
|
||||||
|
|
||||||
// switch screens if mouse is outside screen and not locked to screen
|
|
||||||
IScreen* newScreen = NULL;
|
|
||||||
if (!isLockedToScreen()) {
|
|
||||||
// find direction of neighbor
|
|
||||||
EDirection dir;
|
|
||||||
if (m_x < 0)
|
|
||||||
dir = kLeft;
|
|
||||||
else if (m_x > w - 1)
|
|
||||||
dir = kRight;
|
|
||||||
else if (m_y < 0)
|
|
||||||
dir = kTop;
|
|
||||||
else if (m_y > h - 1)
|
|
||||||
dir = kBottom;
|
|
||||||
else
|
|
||||||
newScreen = m_activeScreen;
|
|
||||||
|
|
||||||
// get neighbor if we should switch
|
|
||||||
if (newScreen == NULL) {
|
|
||||||
TRACE(("leave %s on %s", m_activeScreen->getName().c_str(),
|
|
||||||
s_dirName[dir]));
|
|
||||||
|
|
||||||
SInt32 x = m_x, y = m_y;
|
|
||||||
newScreen = getNeighbor(m_activeScreen, dir, x, y);
|
|
||||||
|
|
||||||
// remap position to account for resolution differences
|
|
||||||
if (newScreen != NULL) {
|
|
||||||
m_x = x;
|
|
||||||
m_y = y;
|
|
||||||
mapPosition(m_activeScreen, dir, newScreen, m_x, m_y);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (m_x < 0)
|
|
||||||
m_x = 0;
|
|
||||||
else if (m_x > w - 1)
|
|
||||||
m_x = w - 1;
|
|
||||||
if (m_y < 0)
|
|
||||||
m_y = 0;
|
|
||||||
else if (m_y > h - 1)
|
|
||||||
m_y = h - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clamp mouse position if locked to screen
|
|
||||||
else {
|
|
||||||
TRACE(("clamp to %s", m_activeScreen->getName().c_str()));
|
|
||||||
|
|
||||||
if (m_x < 0)
|
|
||||||
m_x = 0;
|
|
||||||
else if (m_x > w - 1)
|
|
||||||
m_x = w - 1;
|
|
||||||
if (m_y < 0)
|
|
||||||
m_y = 0;
|
|
||||||
else if (m_y > h - 1)
|
|
||||||
m_y = h - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if on same screen then warp cursor
|
|
||||||
if (newScreen == NULL || newScreen == m_activeScreen) {
|
|
||||||
// ignore if clamped mouse didn't move
|
|
||||||
if (m_x != xOld || m_y != yOld) {
|
|
||||||
TRACE(("move on %s to %d,%d",
|
|
||||||
m_activeScreen->getName().c_str(), m_x, m_y));
|
|
||||||
m_activeScreen->onMouseMove(m_x, m_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise switch the screen
|
|
||||||
else {
|
|
||||||
switchScreen(newScreen, m_x, m_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CServer::isLockedToScreen() const
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CServer::mapPosition(
|
|
||||||
const IScreen* src, EDirection srcSide,
|
|
||||||
const IScreen* dst, SInt32& x, SInt32& y) const
|
|
||||||
{
|
|
||||||
assert(src != NULL);
|
|
||||||
assert(dst != NULL);
|
|
||||||
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
|
|
||||||
|
|
||||||
// get sizes
|
|
||||||
SInt32 wSrc, hSrc, wDst, hDst;
|
|
||||||
src->getSize(&wSrc, &hSrc);
|
|
||||||
dst->getSize(&wDst, &hDst);
|
|
||||||
|
|
||||||
// remap
|
|
||||||
switch (srcSide) {
|
|
||||||
case kLeft:
|
|
||||||
case kRight:
|
|
||||||
assert(y >= 0 && y < hSrc);
|
|
||||||
y = static_cast<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;
|
|
||||||
}
|
|
103
CServer.h
103
CServer.h
|
@ -1,103 +0,0 @@
|
||||||
#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
|
|
58
CSocket.cpp
58
CSocket.cpp
|
@ -1,58 +0,0 @@
|
||||||
#include "CSocket.h"
|
|
||||||
#include "IJob.h"
|
|
||||||
|
|
||||||
//
|
|
||||||
// CSocket
|
|
||||||
//
|
|
||||||
|
|
||||||
CSocket::CSocket() : m_readJob(NULL), m_writeJob(NULL)
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
CSocket::~CSocket()
|
|
||||||
{
|
|
||||||
delete m_readJob;
|
|
||||||
delete m_writeJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSocket::setReadJob(IJob* adoptedJob)
|
|
||||||
{
|
|
||||||
delete m_readJob;
|
|
||||||
m_readJob = adoptedJob;
|
|
||||||
onJobChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSocket::setWriteJob(IJob* adoptedJob)
|
|
||||||
{
|
|
||||||
delete m_writeJob;
|
|
||||||
m_writeJob = adoptedJob;
|
|
||||||
onJobChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSocket::onJobChanged()
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSocket::runReadJob()
|
|
||||||
{
|
|
||||||
if (m_readJob)
|
|
||||||
m_readJob->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSocket::runWriteJob()
|
|
||||||
{
|
|
||||||
if (m_writeJob)
|
|
||||||
m_writeJob->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSocket::hasReadJob() const
|
|
||||||
{
|
|
||||||
return (m_readJob != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CSocket::hasWriteJob() const
|
|
||||||
{
|
|
||||||
return (m_writeJob != NULL);
|
|
||||||
}
|
|
41
CSocket.h
41
CSocket.h
|
@ -1,41 +0,0 @@
|
||||||
#ifndef CSOCKET_H
|
|
||||||
#define CSOCKET_H
|
|
||||||
|
|
||||||
#include "ISocket.h"
|
|
||||||
|
|
||||||
class IJob;
|
|
||||||
|
|
||||||
class CSocket : public ISocket {
|
|
||||||
public:
|
|
||||||
CSocket();
|
|
||||||
virtual ~CSocket();
|
|
||||||
|
|
||||||
// ISocket overrides
|
|
||||||
virtual void setReadJob(IJob* adoptedJob);
|
|
||||||
virtual void setWriteJob(IJob* adoptedJob);
|
|
||||||
virtual void connect(const CString& hostname, UInt16 port) = 0;
|
|
||||||
virtual void listen(const CString& hostname, UInt16 port) = 0;
|
|
||||||
virtual ISocket* accept() = 0;
|
|
||||||
virtual SInt32 read(void* buffer, SInt32 numBytes) = 0;
|
|
||||||
virtual void write(const void* buffer, SInt32 numBytes) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// called when the read or write job is changed. default does nothing.
|
|
||||||
virtual void onJobChanged();
|
|
||||||
|
|
||||||
// subclasses should call these at the appropriate time. different
|
|
||||||
// platforms will arrange to detect these situations differently.
|
|
||||||
// does nothing if the respective job is NULL.
|
|
||||||
void runReadJob();
|
|
||||||
void runWriteJob();
|
|
||||||
|
|
||||||
// return true iff the socket has a read job or a write job
|
|
||||||
bool hasReadJob() const;
|
|
||||||
bool hasWriteJob() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
IJob* m_readJob;
|
|
||||||
IJob* m_writeJob;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,31 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
#ifndef CSOCKETFACTORY_H
|
|
||||||
#define CSOCKETFACTORY_H
|
|
||||||
|
|
||||||
#define CSOCKETFACTORY CSocketFactory::getInstance()
|
|
||||||
|
|
||||||
class ISocket;
|
|
||||||
|
|
||||||
class CSocketFactory {
|
|
||||||
public:
|
|
||||||
CSocketFactory();
|
|
||||||
virtual ~CSocketFactory();
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
static void setInstance(CSocketFactory*);
|
|
||||||
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
// create a socket
|
|
||||||
virtual ISocket* create() const = 0;
|
|
||||||
|
|
||||||
// get the global instance
|
|
||||||
static CSocketFactory* getInstance();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static CSocketFactory* s_instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
40
CString.h
40
CString.h
|
@ -1,40 +0,0 @@
|
||||||
#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
|
|
||||||
|
|
16
CTrace.cpp
16
CTrace.cpp
|
@ -1,16 +0,0 @@
|
||||||
#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");
|
|
||||||
}
|
|
19
CTrace.h
19
CTrace.h
|
@ -1,19 +0,0 @@
|
||||||
#ifndef CTRACE_H
|
|
||||||
#define CTRACE_H
|
|
||||||
|
|
||||||
class CTrace {
|
|
||||||
public:
|
|
||||||
static void print(const char* fmt, ...);
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(NDEBUG)
|
|
||||||
|
|
||||||
#define TRACE(_X)
|
|
||||||
|
|
||||||
#else // NDEBUG
|
|
||||||
|
|
||||||
#define TRACE(_X) CTrace::print ## _X
|
|
||||||
|
|
||||||
#endif // NDEBUG
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,141 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,54 +0,0 @@
|
||||||
#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
|
|
|
@ -1,278 +0,0 @@
|
||||||
#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 <netinet/tcp.h> // FIXME -- for disabling nagle algorithm
|
|
||||||
#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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// always send immediately
|
|
||||||
setNoDelay();
|
|
||||||
}
|
|
||||||
|
|
||||||
CUnixTCPSocket::CUnixTCPSocket(int fd) : m_fd(fd),
|
|
||||||
m_state(kConnected),
|
|
||||||
m_addedJobs(false)
|
|
||||||
{
|
|
||||||
assert(m_fd != -1);
|
|
||||||
setNoDelay();
|
|
||||||
}
|
|
||||||
|
|
||||||
CUnixTCPSocket::~CUnixTCPSocket()
|
|
||||||
{
|
|
||||||
assert(m_fd != -1);
|
|
||||||
|
|
||||||
// unhook events
|
|
||||||
if (m_addedJobs)
|
|
||||||
CEQ->removeFileDesc(m_fd);
|
|
||||||
|
|
||||||
// drain socket
|
|
||||||
if (m_state == kConnected)
|
|
||||||
::shutdown(m_fd, 0);
|
|
||||||
|
|
||||||
// close socket
|
|
||||||
::close(m_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUnixTCPSocket::onJobChanged()
|
|
||||||
{
|
|
||||||
// remove old jobs
|
|
||||||
if (m_addedJobs) {
|
|
||||||
CEQ->removeFileDesc(m_fd);
|
|
||||||
m_addedJobs = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// which jobs should we install?
|
|
||||||
bool doRead = false;
|
|
||||||
bool doWrite = false;
|
|
||||||
switch (m_state) {
|
|
||||||
case kNone:
|
|
||||||
return;
|
|
||||||
|
|
||||||
case kConnecting:
|
|
||||||
doWrite = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kConnected:
|
|
||||||
doRead = hasReadJob();
|
|
||||||
doWrite = hasWriteJob();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kListening:
|
|
||||||
doRead = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make jobs
|
|
||||||
IJob* readJob = doRead ? new TMethodJob<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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CUnixTCPSocket::setNoDelay()
|
|
||||||
{
|
|
||||||
// turn off Nagle algorithm. we send lots of really short messages
|
|
||||||
// so we'll accept the (much) larger overhead to reduce latency.
|
|
||||||
struct protoent* p = getprotobyname("tcp");
|
|
||||||
if (p) {
|
|
||||||
int on = 1;
|
|
||||||
setsockopt(m_fd, p->p_proto, TCP_NODELAY, &on, sizeof(on));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
#ifndef CUNIXTCPSOCKET_H
|
|
||||||
#define CUNIXTCPSOCKET_H
|
|
||||||
|
|
||||||
#include "CSocket.h"
|
|
||||||
#include "CSocketFactory.h"
|
|
||||||
|
|
||||||
class CUnixTCPSocket : public CSocket {
|
|
||||||
public:
|
|
||||||
CUnixTCPSocket();
|
|
||||||
virtual ~CUnixTCPSocket();
|
|
||||||
|
|
||||||
// ISocket overrides
|
|
||||||
virtual void connect(const CString& hostname, UInt16 port);
|
|
||||||
virtual void listen(const CString& hostname, UInt16 port);
|
|
||||||
virtual ISocket* accept();
|
|
||||||
virtual SInt32 read(void* buffer, SInt32 numBytes);
|
|
||||||
virtual void write(const void* buffer, SInt32 numBytes);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// CSocket overrides
|
|
||||||
virtual void onJobChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CUnixTCPSocket(int);
|
|
||||||
|
|
||||||
// disable Nagle algorithm
|
|
||||||
void setNoDelay();
|
|
||||||
|
|
||||||
// callbacks for read/write events
|
|
||||||
void readCB();
|
|
||||||
void writeCB();
|
|
||||||
|
|
||||||
private:
|
|
||||||
enum EState { kNone, kConnecting, kConnected, kListening };
|
|
||||||
int m_fd;
|
|
||||||
EState m_state;
|
|
||||||
bool m_addedJobs;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CUnixTCPSocketFactory : public CSocketFactory {
|
|
||||||
public:
|
|
||||||
CUnixTCPSocketFactory() { }
|
|
||||||
virtual ~CUnixTCPSocketFactory() { }
|
|
||||||
|
|
||||||
// CSocketFactory overrides
|
|
||||||
virtual ISocket* create() const
|
|
||||||
{ return new CUnixTCPSocket; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,34 +0,0 @@
|
||||||
#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()));
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
#ifndef CUNIXXSCREEN_H
|
|
||||||
#define CUNIXXSCREEN_H
|
|
||||||
|
|
||||||
#include "CXScreen.h"
|
|
||||||
|
|
||||||
class CUnixXScreen : public CXScreen {
|
|
||||||
public:
|
|
||||||
CUnixXScreen(const CString& name);
|
|
||||||
virtual ~CUnixXScreen();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void onOpen(bool isPrimary);
|
|
||||||
virtual void onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
626
CXScreen.cpp
626
CXScreen.cpp
|
@ -1,626 +0,0 @@
|
||||||
#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 key, KeyModifierMask)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
// FIXME -- use mask
|
|
||||||
::XTestFakeKeyEvent(m_display, mapKeyToX(key), True, CurrentTime);
|
|
||||||
::XSync(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onKeyRepeat(KeyID, KeyModifierMask, SInt32)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onKeyUp(KeyID key, KeyModifierMask)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
// FIXME -- use mask
|
|
||||||
::XTestFakeKeyEvent(m_display, mapKeyToX(key), False, CurrentTime);
|
|
||||||
::XSync(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onMouseDown(ButtonID button)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
::XTestFakeButtonEvent(m_display, mapButtonToX(button), True, CurrentTime);
|
|
||||||
::XSync(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onMouseUp(ButtonID button)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
::XTestFakeButtonEvent(m_display, mapButtonToX(button), False, CurrentTime);
|
|
||||||
::XSync(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onMouseMove(SInt32 x, SInt32 y)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
::XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime);
|
|
||||||
::XSync(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onMouseWheel(SInt32)
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onClipboardChanged()
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(m_primary == false);
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
CString CXScreen::getName() const
|
|
||||||
{
|
|
||||||
return m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::getSize(
|
|
||||||
SInt32* width, SInt32* height) const
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
assert(width != NULL && height != NULL);
|
|
||||||
|
|
||||||
*width = m_w;
|
|
||||||
*height = m_h;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::getClipboard(
|
|
||||||
IClipboard* /*clipboard*/) const
|
|
||||||
{
|
|
||||||
assert(m_display != NULL);
|
|
||||||
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::openPrimary()
|
|
||||||
{
|
|
||||||
// get the root window
|
|
||||||
Window root = RootWindow(m_display, m_screen);
|
|
||||||
|
|
||||||
// create the grab window. this window is used to capture user
|
|
||||||
// input when the user is focussed on another client. don't let
|
|
||||||
// the window manager mess with it.
|
|
||||||
XSetWindowAttributes attr;
|
|
||||||
attr.event_mask = PointerMotionMask |// PointerMotionHintMask |
|
|
||||||
ButtonPressMask | ButtonReleaseMask |
|
|
||||||
KeyPressMask | KeyReleaseMask |
|
|
||||||
KeymapStateMask;
|
|
||||||
attr.do_not_propagate_mask = 0;
|
|
||||||
attr.override_redirect = True;
|
|
||||||
attr.cursor = None;
|
|
||||||
m_window = ::XCreateWindow(m_display, root, 0, 0, m_w, m_h, 0, 0,
|
|
||||||
InputOnly, CopyFromParent,
|
|
||||||
CWDontPropagate | CWEventMask |
|
|
||||||
CWOverrideRedirect | CWCursor,
|
|
||||||
&attr);
|
|
||||||
|
|
||||||
// start watching for events on other windows
|
|
||||||
selectEvents(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::closePrimary()
|
|
||||||
{
|
|
||||||
assert(m_window != None);
|
|
||||||
|
|
||||||
// destroy window
|
|
||||||
::XDestroyWindow(m_display, m_window);
|
|
||||||
m_window = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::enterScreenPrimary(SInt32 x, SInt32 y)
|
|
||||||
{
|
|
||||||
assert(m_window != None);
|
|
||||||
assert(m_active == true);
|
|
||||||
|
|
||||||
// warp to requested location
|
|
||||||
::XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y);
|
|
||||||
|
|
||||||
// unmap the grab window. this also ungrabs the mouse and keyboard.
|
|
||||||
::XUnmapWindow(m_display, m_window);
|
|
||||||
|
|
||||||
// remove all input events for grab window
|
|
||||||
XEvent event;
|
|
||||||
while (::XCheckWindowEvent(m_display, m_window,
|
|
||||||
PointerMotionMask |
|
|
||||||
ButtonPressMask | ButtonReleaseMask |
|
|
||||||
KeyPressMask | KeyReleaseMask |
|
|
||||||
KeymapStateMask,
|
|
||||||
&event))
|
|
||||||
; // do nothing
|
|
||||||
|
|
||||||
// not active anymore
|
|
||||||
m_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::leaveScreenPrimary()
|
|
||||||
{
|
|
||||||
assert(m_window != None);
|
|
||||||
assert(m_active == false);
|
|
||||||
|
|
||||||
// raise and show the input window
|
|
||||||
::XMapRaised(m_display, m_window);
|
|
||||||
|
|
||||||
// grab the mouse and keyboard. keep trying until we get them.
|
|
||||||
// if we can't grab one after grabbing the other then ungrab
|
|
||||||
// and wait before retrying.
|
|
||||||
int result;
|
|
||||||
do {
|
|
||||||
// mouse first
|
|
||||||
do {
|
|
||||||
result = ::XGrabPointer(m_display, m_window, True, 0,
|
|
||||||
GrabModeAsync, GrabModeAsync,
|
|
||||||
m_window, None, CurrentTime);
|
|
||||||
assert(result != GrabNotViewable);
|
|
||||||
if (result != GrabSuccess)
|
|
||||||
::sleep(1);
|
|
||||||
} while (result != GrabSuccess);
|
|
||||||
|
|
||||||
// now the keyboard
|
|
||||||
result = ::XGrabKeyboard(m_display, m_window, True,
|
|
||||||
GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
||||||
assert(result != GrabNotViewable);
|
|
||||||
if (result != GrabSuccess) {
|
|
||||||
::XUngrabPointer(m_display, CurrentTime);
|
|
||||||
::sleep(1);
|
|
||||||
}
|
|
||||||
} while (result != GrabSuccess);
|
|
||||||
|
|
||||||
// move the mouse to the center of grab window
|
|
||||||
warpCursor(m_w >> 1, m_h >> 1);
|
|
||||||
|
|
||||||
// local client now active
|
|
||||||
m_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::setClipboardPrimary(
|
|
||||||
const IClipboard* /*clipboard*/)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onScreenSaverPrimary(bool /*show*/)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::openSecondary()
|
|
||||||
{
|
|
||||||
// verify the availability of the XTest extension
|
|
||||||
int majorOpcode, firstEvent, firstError;
|
|
||||||
if (!::XQueryExtension(m_display, XTestExtensionName,
|
|
||||||
&majorOpcode, &firstEvent, &firstError))
|
|
||||||
throw XClientOpen();
|
|
||||||
|
|
||||||
// become impervious to server grabs
|
|
||||||
XTestGrabControl(m_display, True);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::closeSecondary()
|
|
||||||
{
|
|
||||||
// no longer impervious to server grabs
|
|
||||||
XTestGrabControl(m_display, False);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::enterScreenSecondary(
|
|
||||||
SInt32 x, SInt32 y)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::leaveScreenSecondary()
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::setClipboardSecondary(
|
|
||||||
const IClipboard* /*clipboard*/)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onScreenSaverSecondary(bool /*show*/)
|
|
||||||
{
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
|
|
||||||
Display* CXScreen::getDisplay() const
|
|
||||||
{
|
|
||||||
return m_display;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onEvents()
|
|
||||||
{
|
|
||||||
if (m_primary)
|
|
||||||
onPrimaryEvents();
|
|
||||||
else
|
|
||||||
onSecondaryEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::selectEvents(Window w) const
|
|
||||||
{
|
|
||||||
// we want to track the mouse everywhere on the display. to achieve
|
|
||||||
// that we select PointerMotionMask on every window. we also select
|
|
||||||
// SubstructureNotifyMask in order to get CreateNotify events so we
|
|
||||||
// select events on new windows too.
|
|
||||||
|
|
||||||
// we don't want to adjust our grab window
|
|
||||||
if (w == m_window)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// select events of interest
|
|
||||||
::XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask);
|
|
||||||
|
|
||||||
// recurse on child windows
|
|
||||||
Window rw, pw, *cw;
|
|
||||||
unsigned int nc;
|
|
||||||
if (::XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
|
|
||||||
for (unsigned int i = 0; i < nc; ++i)
|
|
||||||
selectEvents(cw[i]);
|
|
||||||
::XFree(cw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyModifierMask CXScreen::mapModifierFromX(unsigned int state) const
|
|
||||||
{
|
|
||||||
// FIXME -- should be configurable
|
|
||||||
KeyModifierMask mask = 0;
|
|
||||||
if (state & 1)
|
|
||||||
mask |= KeyModifierShift;
|
|
||||||
if (state & 2)
|
|
||||||
mask |= KeyModifierCapsLock;
|
|
||||||
if (state & 4)
|
|
||||||
mask |= KeyModifierControl;
|
|
||||||
if (state & 8)
|
|
||||||
mask |= KeyModifierAlt;
|
|
||||||
if (state & 16)
|
|
||||||
mask |= KeyModifierNumLock;
|
|
||||||
if (state & 32)
|
|
||||||
mask |= KeyModifierMeta;
|
|
||||||
if (state & 128)
|
|
||||||
mask |= KeyModifierScrollLock;
|
|
||||||
return mask;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CXScreen::mapModifierToX(KeyModifierMask mask) const
|
|
||||||
{
|
|
||||||
// FIXME -- should be configurable
|
|
||||||
unsigned int state = 0;
|
|
||||||
if (mask & KeyModifierShift)
|
|
||||||
state |= 1;
|
|
||||||
if (mask & KeyModifierControl)
|
|
||||||
state |= 4;
|
|
||||||
if (mask & KeyModifierAlt)
|
|
||||||
state |= 8;
|
|
||||||
if (mask & KeyModifierMeta)
|
|
||||||
state |= 32;
|
|
||||||
if (mask & KeyModifierCapsLock)
|
|
||||||
state |= 2;
|
|
||||||
if (mask & KeyModifierNumLock)
|
|
||||||
state |= 16;
|
|
||||||
if (mask & KeyModifierScrollLock)
|
|
||||||
state |= 128;
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyID CXScreen::mapKeyFromX(
|
|
||||||
KeyCode keycode, KeyModifierMask mask) const
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
if (mask & KeyModifierShift)
|
|
||||||
index = 1;
|
|
||||||
else
|
|
||||||
index = 0;
|
|
||||||
return static_cast<KeyID>(::XKeycodeToKeysym(m_display, keycode, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
KeyCode CXScreen::mapKeyToX(KeyID keyID) const
|
|
||||||
{
|
|
||||||
return ::XKeysymToKeycode(m_display, static_cast<KeySym>(keyID));
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonID CXScreen::mapButtonFromX(unsigned int button) const
|
|
||||||
{
|
|
||||||
// FIXME -- should use button mapping?
|
|
||||||
if (button >= 1 && button <= 3)
|
|
||||||
return static_cast<ButtonID>(button);
|
|
||||||
else
|
|
||||||
return kButtonNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CXScreen::mapButtonToX(ButtonID buttonID) const
|
|
||||||
{
|
|
||||||
// FIXME -- should use button mapping?
|
|
||||||
return static_cast<unsigned int>(buttonID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onPrimaryEvents()
|
|
||||||
{
|
|
||||||
while (XPending(m_display) > 0) {
|
|
||||||
XEvent xevent;
|
|
||||||
XNextEvent(m_display, &xevent);
|
|
||||||
|
|
||||||
switch (xevent.type) {
|
|
||||||
case KeyPress: {
|
|
||||||
const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state);
|
|
||||||
const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask);
|
|
||||||
if (key != kKeyNone) {
|
|
||||||
CEvent event;
|
|
||||||
event.m_key.m_type = CEventBase::kKeyDown;
|
|
||||||
event.m_key.m_key = key;
|
|
||||||
event.m_key.m_mask = mask;
|
|
||||||
event.m_key.m_count = 0;
|
|
||||||
CEQ->push(&event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME -- simulate key repeat. X sends press/release for
|
|
||||||
// repeat. must detect auto repeat and use kKeyRepeat.
|
|
||||||
case KeyRelease: {
|
|
||||||
const KeyModifierMask mask = mapModifierFromX(xevent.xkey.state);
|
|
||||||
const KeyID key = mapKeyFromX(xevent.xkey.keycode, mask);
|
|
||||||
if (key != kKeyNone) {
|
|
||||||
CEvent event;
|
|
||||||
event.m_key.m_type = CEventBase::kKeyUp;
|
|
||||||
event.m_key.m_key = key;
|
|
||||||
event.m_key.m_mask = mask;
|
|
||||||
event.m_key.m_count = 0;
|
|
||||||
CEQ->push(&event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ButtonPress: {
|
|
||||||
const ButtonID button = mapButtonFromX(xevent.xbutton.button);
|
|
||||||
if (button != kButtonNone) {
|
|
||||||
CEvent event;
|
|
||||||
event.m_mouse.m_type = CEventBase::kMouseDown;
|
|
||||||
event.m_mouse.m_button = button;
|
|
||||||
event.m_mouse.m_x = 0;
|
|
||||||
event.m_mouse.m_y = 0;
|
|
||||||
CEQ->push(&event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case ButtonRelease: {
|
|
||||||
const ButtonID button = mapButtonFromX(xevent.xbutton.button);
|
|
||||||
if (button != kButtonNone) {
|
|
||||||
CEvent event;
|
|
||||||
event.m_mouse.m_type = CEventBase::kMouseUp;
|
|
||||||
event.m_mouse.m_button = button;
|
|
||||||
event.m_mouse.m_x = 0;
|
|
||||||
event.m_mouse.m_y = 0;
|
|
||||||
CEQ->push(&event);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MotionNotify: {
|
|
||||||
CEvent event;
|
|
||||||
event.m_mouse.m_type = CEventBase::kMouseMove;
|
|
||||||
event.m_mouse.m_button = kButtonNone;
|
|
||||||
if (!m_active) {
|
|
||||||
event.m_mouse.m_x = xevent.xmotion.x_root;
|
|
||||||
event.m_mouse.m_y = xevent.xmotion.y_root;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// FIXME -- slurp up all remaining motion events?
|
|
||||||
// probably not since key strokes may go to wrong place.
|
|
||||||
|
|
||||||
// get mouse deltas
|
|
||||||
Window root, window;
|
|
||||||
int xRoot, yRoot, xWindow, yWindow;
|
|
||||||
unsigned int mask;
|
|
||||||
if (!::XQueryPointer(m_display, m_window, &root, &window,
|
|
||||||
&xRoot, &yRoot, &xWindow, &yWindow, &mask))
|
|
||||||
break;
|
|
||||||
event.m_mouse.m_x = xRoot - (m_w >> 1);
|
|
||||||
event.m_mouse.m_y = yRoot - (m_h >> 1);
|
|
||||||
|
|
||||||
// warp mouse back to center
|
|
||||||
warpCursor(m_w >> 1, m_h >> 1);
|
|
||||||
}
|
|
||||||
CEQ->push(&event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case CreateNotify:
|
|
||||||
// select events on new window
|
|
||||||
if (m_primary)
|
|
||||||
selectEvents(xevent.xcreatewindow.window);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*
|
|
||||||
case SelectionClear:
|
|
||||||
target->XXX(xevent.xselectionclear.);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SelectionNotify:
|
|
||||||
target->XXX(xevent.xselection.);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SelectionRequest:
|
|
||||||
target->XXX(xevent.xselectionrequest.);
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CXScreen::onSecondaryEvents()
|
|
||||||
{
|
|
||||||
while (XPending(m_display) > 0) {
|
|
||||||
XEvent xevent;
|
|
||||||
XNextEvent(m_display, &xevent);
|
|
||||||
// FIXME
|
|
||||||
}
|
|
||||||
}
|
|
84
CXScreen.h
84
CXScreen.h
|
@ -1,84 +0,0 @@
|
||||||
#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, KeyModifierMask);
|
|
||||||
virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32);
|
|
||||||
virtual void onKeyUp(KeyID, KeyModifierMask);
|
|
||||||
virtual void onMouseDown(ButtonID);
|
|
||||||
virtual void onMouseUp(ButtonID);
|
|
||||||
virtual void onMouseMove(SInt32, SInt32);
|
|
||||||
virtual void onMouseWheel(SInt32);
|
|
||||||
virtual void onClipboardChanged();
|
|
||||||
virtual CString getName() const;
|
|
||||||
virtual void getSize(SInt32* width, SInt32* height) const;
|
|
||||||
virtual void getClipboard(IClipboard*) const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// primary screen implementations
|
|
||||||
virtual void openPrimary();
|
|
||||||
virtual void closePrimary();
|
|
||||||
virtual void enterScreenPrimary(SInt32 x, SInt32 y);
|
|
||||||
virtual void leaveScreenPrimary();
|
|
||||||
virtual void setClipboardPrimary(const IClipboard*);
|
|
||||||
virtual void onScreenSaverPrimary(bool);
|
|
||||||
|
|
||||||
// secondary screen implementations
|
|
||||||
virtual void openSecondary();
|
|
||||||
virtual void closeSecondary();
|
|
||||||
virtual void enterScreenSecondary(SInt32 x, SInt32 y);
|
|
||||||
virtual void leaveScreenSecondary();
|
|
||||||
virtual void setClipboardSecondary(const IClipboard*);
|
|
||||||
virtual void onScreenSaverSecondary(bool);
|
|
||||||
|
|
||||||
// get the display
|
|
||||||
Display* getDisplay() const;
|
|
||||||
|
|
||||||
// process X events from the display
|
|
||||||
void onEvents();
|
|
||||||
|
|
||||||
// called by open() and close(). override to hook up and unhook the
|
|
||||||
// display connection to the event queue. call onEvents() when events
|
|
||||||
// are available.
|
|
||||||
virtual void onOpen(bool isPrimary) = 0;
|
|
||||||
virtual void onClose() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void selectEvents(Window) const;
|
|
||||||
KeyModifierMask mapModifierFromX(unsigned int) const;
|
|
||||||
unsigned int mapModifierToX(KeyModifierMask) const;
|
|
||||||
KeyID mapKeyFromX(KeyCode, KeyModifierMask) const;
|
|
||||||
KeyCode mapKeyToX(KeyID) const;
|
|
||||||
ButtonID mapButtonFromX(unsigned int button) const;
|
|
||||||
unsigned int mapButtonToX(ButtonID) const;
|
|
||||||
void onPrimaryEvents();
|
|
||||||
void onSecondaryEvents();
|
|
||||||
|
|
||||||
private:
|
|
||||||
CString m_name;
|
|
||||||
Display* m_display;
|
|
||||||
int m_screen;
|
|
||||||
bool m_primary;
|
|
||||||
SInt32 m_w, m_h;
|
|
||||||
|
|
||||||
// stuff for primary screens
|
|
||||||
Window m_window;
|
|
||||||
bool m_active;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
17
IClient.h
17
IClient.h
|
@ -1,17 +0,0 @@
|
||||||
#ifndef ICLIENT_H
|
|
||||||
#define ICLIENT_H
|
|
||||||
|
|
||||||
class CString;
|
|
||||||
|
|
||||||
class IClient {
|
|
||||||
public:
|
|
||||||
IClient() { }
|
|
||||||
virtual ~IClient() { }
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
// connect to server and begin processing events
|
|
||||||
virtual void run(const CString& hostname) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,45 +0,0 @@
|
||||||
#ifndef IEVENTQUEUE_H
|
|
||||||
#define IEVENTQUEUE_H
|
|
||||||
|
|
||||||
#define CEQ (IEventQueue::getInstance())
|
|
||||||
|
|
||||||
class CEvent;
|
|
||||||
|
|
||||||
class IEventQueue {
|
|
||||||
public:
|
|
||||||
IEventQueue();
|
|
||||||
virtual ~IEventQueue();
|
|
||||||
|
|
||||||
// note -- all of the methods in an IEventQueue subclass for a
|
|
||||||
// platform must be thread safe if it will be used by multiple
|
|
||||||
// threads simultaneously on that platform.
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
// wait up to timeout seconds for the queue to become not empty.
|
|
||||||
// as a side effect this can do the insertion of events. if
|
|
||||||
// timeout < 0.0 then wait indefinitely. it's possible for
|
|
||||||
// wait() to return prematurely so always call isEmpty() to
|
|
||||||
// see if there are any events.
|
|
||||||
virtual void wait(double timeout) = 0;
|
|
||||||
|
|
||||||
// reads and removes the next event on the queue. waits indefinitely
|
|
||||||
// for an event if the queue is empty.
|
|
||||||
virtual void pop(CEvent*) = 0;
|
|
||||||
|
|
||||||
// push an event onto the queue
|
|
||||||
virtual void push(const CEvent*) = 0;
|
|
||||||
|
|
||||||
// returns true if the queue is empty and wait() would block
|
|
||||||
virtual bool isEmpty() = 0;
|
|
||||||
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
// get the singleton event queue
|
|
||||||
static IEventQueue* getInstance();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static IEventQueue* s_instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
130
IScreen.h
130
IScreen.h
|
@ -1,130 +0,0 @@
|
||||||
#ifndef ISCREEN_H
|
|
||||||
#define ISCREEN_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IScreen -- interface for display screens
|
|
||||||
*
|
|
||||||
* a screen encapsulates input and output devices, typically a mouse
|
|
||||||
* and keyboard for input and a graphical display for output. one
|
|
||||||
* screen is designated as the primary screen. only input from the
|
|
||||||
* primary screen's input devices is used. other screens are secondary
|
|
||||||
* screens and they simulate input from their input devices but ignore
|
|
||||||
* any actual input. a screen can be either a primary or a secondary
|
|
||||||
* but not both at the same time. most methods behave differently
|
|
||||||
* depending on the screen type.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "BasicTypes.h"
|
|
||||||
#include "KeyTypes.h"
|
|
||||||
#include "MouseTypes.h"
|
|
||||||
#include "CString.h"
|
|
||||||
|
|
||||||
class IClipboard;
|
|
||||||
|
|
||||||
class IScreen {
|
|
||||||
public:
|
|
||||||
IScreen() { }
|
|
||||||
virtual ~IScreen() { }
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
// open/close screen. these are where the client should do
|
|
||||||
// initialization and cleanup of the system's screen. if isPrimary
|
|
||||||
// is true then this screen will be used (exclusively) as the
|
|
||||||
// primary screen, otherwise it will be used (exclusively) as a
|
|
||||||
// secondary screen.
|
|
||||||
//
|
|
||||||
// primary:
|
|
||||||
// open(): open the screen and begin reporting input events to
|
|
||||||
// the event queue. input events should be reported no matter
|
|
||||||
// where on the screen they occur but the screen should not
|
|
||||||
// interfere with the normal dispatching of events. the screen
|
|
||||||
// should detect when the screen saver is activated. if it can't
|
|
||||||
// do that it should disable the screen saver and start it itself
|
|
||||||
// after the appropriate duration of no input.
|
|
||||||
//
|
|
||||||
// secondary:
|
|
||||||
// open(): open the screen, hide the cursor and disable the
|
|
||||||
// screen saver. then wait for an enterScreen() or close(),
|
|
||||||
// reporting the following events: FIXME.
|
|
||||||
virtual void open(bool isPrimary) = 0;
|
|
||||||
virtual void close() = 0;
|
|
||||||
|
|
||||||
// enter/leave screen
|
|
||||||
//
|
|
||||||
// primary:
|
|
||||||
// enterScreen(): the user has navigated back to the primary
|
|
||||||
// screen. warp the cursor to the given coordinates, unhide the
|
|
||||||
// cursor and ungrab the input devices. the screen must also
|
|
||||||
// detect and report (enqueue) input events. for the primary
|
|
||||||
// screen, enterScreen() is only called after a leaveScreen().
|
|
||||||
// leaveScreen(): the user has navigated off the primary screen.
|
|
||||||
// hide the cursor and grab exclusive access to the input devices.
|
|
||||||
// input events must be reported.
|
|
||||||
//
|
|
||||||
// secondary:
|
|
||||||
// enterScreen(): the user has navigated to this secondary
|
|
||||||
// screen. warp the cursor to the given coordinates and show it.
|
|
||||||
// prepare to simulate input events.
|
|
||||||
// leaveScreen(): the user has navigated off this secondary
|
|
||||||
// screen. clean up input event simulation. hide the cursor.
|
|
||||||
virtual void enterScreen(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
|
||||||
virtual void leaveScreen() = 0;
|
|
||||||
|
|
||||||
// warp the cursor to the given position
|
|
||||||
virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// clipboard operations
|
|
||||||
//
|
|
||||||
|
|
||||||
// set the screen's clipboard contents. this is usually called
|
|
||||||
// soon after an enterScreen().
|
|
||||||
virtual void setClipboard(const IClipboard*) = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// screen saver operations
|
|
||||||
//
|
|
||||||
|
|
||||||
// show or hide the screen saver
|
|
||||||
virtual void onScreenSaver(bool show) = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// input simulation
|
|
||||||
//
|
|
||||||
// these methods must simulate the appropriate input event.
|
|
||||||
// these methods are only called on secondary screens.
|
|
||||||
//
|
|
||||||
|
|
||||||
// keyboard input
|
|
||||||
virtual void onKeyDown(KeyID, KeyModifierMask) = 0;
|
|
||||||
virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0;
|
|
||||||
virtual void onKeyUp(KeyID, KeyModifierMask) = 0;
|
|
||||||
|
|
||||||
// mouse input
|
|
||||||
virtual void onMouseDown(ButtonID) = 0;
|
|
||||||
virtual void onMouseUp(ButtonID) = 0;
|
|
||||||
virtual void onMouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0;
|
|
||||||
virtual void onMouseWheel(SInt32 delta) = 0;
|
|
||||||
|
|
||||||
// clipboard input
|
|
||||||
// FIXME -- do we need this?
|
|
||||||
virtual void onClipboardChanged() = 0;
|
|
||||||
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
// get the screen's name. all screens must have a name unique on
|
|
||||||
// the server they connect to. the hostname is usually an
|
|
||||||
// appropriate name.
|
|
||||||
virtual CString getName() const = 0;
|
|
||||||
|
|
||||||
// get the size of the screen
|
|
||||||
virtual void getSize(SInt32* width, SInt32* height) const = 0;
|
|
||||||
|
|
||||||
// clipboard operations
|
|
||||||
|
|
||||||
// get the screen's clipboard contents
|
|
||||||
virtual void getClipboard(IClipboard*) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
29
IServer.h
29
IServer.h
|
@ -1,29 +0,0 @@
|
||||||
#ifndef ISERVER_H
|
|
||||||
#define ISERVER_H
|
|
||||||
|
|
||||||
class IScreen;
|
|
||||||
|
|
||||||
class IServer {
|
|
||||||
public:
|
|
||||||
IServer() { }
|
|
||||||
virtual ~IServer() { }
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
// run the server until terminated
|
|
||||||
virtual void run() = 0;
|
|
||||||
|
|
||||||
// clipboard operations
|
|
||||||
virtual void onClipboardChanged(IScreen*) = 0;
|
|
||||||
|
|
||||||
// enter the given screen, leaving the previous screen. the cursor
|
|
||||||
// should be warped to the center of the screen.
|
|
||||||
virtual void setActiveScreen(IScreen*) = 0;
|
|
||||||
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
// get the screen that was last entered
|
|
||||||
virtual IScreen* getActiveScreen() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
45
ISocket.h
45
ISocket.h
|
@ -1,45 +0,0 @@
|
||||||
#ifndef ISOCKET_H
|
|
||||||
#define ISOCKET_H
|
|
||||||
|
|
||||||
#include "BasicTypes.h"
|
|
||||||
|
|
||||||
class IJob;
|
|
||||||
class CString;
|
|
||||||
|
|
||||||
class ISocket {
|
|
||||||
public:
|
|
||||||
// d'tor closes the socket
|
|
||||||
ISocket() { }
|
|
||||||
virtual ~ISocket() { }
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
// set the job to invoke when the socket is readable or writable.
|
|
||||||
// a socket that has connected after a call to connect() becomes
|
|
||||||
// writable. a socket that is ready to accept a connection after
|
|
||||||
// a call to listen() becomes readable. the socket returned by
|
|
||||||
// accept() does not have any jobs assigned to it.
|
|
||||||
virtual void setReadJob(IJob* adoptedJob) = 0;
|
|
||||||
virtual void setWriteJob(IJob* adoptedJob) = 0;
|
|
||||||
|
|
||||||
// open/close. connect() begins connecting to the given host but
|
|
||||||
// doesn't wait for the connection to complete. listen() begins
|
|
||||||
// listening on the given interface and port; if hostname is
|
|
||||||
// empty then listen on all interfaces. accept() waits for a
|
|
||||||
// connection on the listening interface and returns a new
|
|
||||||
// socket for the connection.
|
|
||||||
virtual void connect(const CString& hostname, UInt16 port) = 0;
|
|
||||||
virtual void listen(const CString& hostname, UInt16 port) = 0;
|
|
||||||
virtual ISocket* accept() = 0;
|
|
||||||
|
|
||||||
// read data from socket. returns without waiting if not enough
|
|
||||||
// data is available. returns the number of bytes actually read,
|
|
||||||
// which is zero if there were no bytes to read and -1 if the
|
|
||||||
// remote end of the socket has disconnected.
|
|
||||||
virtual SInt32 read(void* buffer, SInt32 numBytes) = 0;
|
|
||||||
|
|
||||||
// write data to socket. waits until all data has been written.
|
|
||||||
virtual void write(const void* buffer, SInt32 numBytes) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
#
|
||||||
|
# build tools
|
||||||
|
#
|
||||||
|
AR = /usr/ccs/bin/ar cru
|
||||||
|
CD = cd
|
||||||
|
CXX = g++
|
||||||
|
ECHO = echo
|
||||||
|
LD = /usr/css/bin/ld
|
||||||
|
MKDIR = /bin/mkdir
|
||||||
|
RM = /bin/rm -f
|
||||||
|
RMR = /bin/rm -rf
|
||||||
|
|
||||||
|
#
|
||||||
|
# compiler options
|
||||||
|
#
|
||||||
|
GCXXDEFS =
|
||||||
|
GCXXINCS = -I$(DEPTH)/include -I/usr/X11R6/include
|
||||||
|
GCXXOPTS = -Wall -W -fexceptions -fno-rtti
|
||||||
|
CXXOPTIMIZER = -g
|
||||||
|
#CXXOPTIMIZER = -O2 -DNDEBUG
|
||||||
|
|
||||||
|
#
|
||||||
|
# linker options
|
||||||
|
#
|
||||||
|
#GLDLIBS = -L$(LIBDIR) -L/usr/X11R6/lib -lX11 -lXext -lXtst
|
||||||
|
GLDLIBS = -L$(LIBDIR) -lsocket -lnsl -lposix4
|
||||||
|
GLDOPTS = -z muldefs
|
||||||
|
|
||||||
|
#
|
||||||
|
# library stuff
|
||||||
|
#
|
||||||
|
LIBTARGET = $(LIBDIR)/lib$(TARGET).a
|
||||||
|
|
||||||
|
#
|
||||||
|
# dependency generation stuff
|
||||||
|
#
|
||||||
|
MKDEP = $(DEPTH)/tools/depconv
|
||||||
|
MKDEPOPT = -MD
|
||||||
|
MKDEPPRE =
|
||||||
|
MKDEPPOST = $(MKDEP) -f $(MKDEPFILE) $*.d; $(RM) $*.d
|
||||||
|
MKDEPFILE = Makedepend
|
||||||
|
|
||||||
|
#
|
||||||
|
# stuff to clean
|
||||||
|
#
|
||||||
|
DIRT = $(_FORCE) $(LDIRT) $(GDIRT)
|
||||||
|
GDIRT = *.[eoud] a.out core ar.tmp.* $(MKDEPFILE)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Rule macros for nonterminal makefiles that iterate over subdirectories,
|
||||||
|
# making the current target. Set SUBDIRS to the relevant list of kids.
|
||||||
|
#
|
||||||
|
# Set NOSUBMESG to any value to suppress a warning that subdirectories
|
||||||
|
# are not present.
|
||||||
|
#
|
||||||
|
SUBDIR_MAKERULE= \
|
||||||
|
if test ! -d $$d; then \
|
||||||
|
if test "$(NOSUBMESG)" = "" ; then \
|
||||||
|
${ECHO} "SKIPPING $$d: No such directory."; \
|
||||||
|
fi \
|
||||||
|
else \
|
||||||
|
${ECHO} "${CD} $$d; $(MAKE) $${RULE:=$@}"; \
|
||||||
|
(${CD} $$d; ${MAKE} $${RULE:=$@}); \
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUBDIRS_MAKERULE= \
|
||||||
|
@for d in $(SUBDIRS); do $(SUBDIR_MAKERULE); done
|
|
@ -0,0 +1,93 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# common definitions
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# empty define, used to terminate lists
|
||||||
|
#
|
||||||
|
NULL =
|
||||||
|
|
||||||
|
#
|
||||||
|
# target directories
|
||||||
|
#
|
||||||
|
LIBDIR = $(DEPTH)/lib
|
||||||
|
TARGETDIR = $(DEPTH)/bin
|
||||||
|
|
||||||
|
#
|
||||||
|
# compiler options
|
||||||
|
#
|
||||||
|
CXXFLAGS = $(LCXXFLAGS) $(GCXXFLAGS) $(CXXOPTIMIZER) $(MKDEPOPT)
|
||||||
|
LCXXFLAGS = $(LCXXDEFS) $(LCXXINCS) $(LCXXOPTS)
|
||||||
|
GCXXFLAGS = $(GCXXDEFS) $(GCXXINCS) $(GCXXOPTS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# linker options
|
||||||
|
#
|
||||||
|
LDFLAGS = $(LDOPTS) $(LDLIBS)
|
||||||
|
LDOPTS = $(LLDOPTS) $(GLDOPTS)
|
||||||
|
LDLIBS = $(LLDLIBS) $(GLDLIBS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# ar options
|
||||||
|
#
|
||||||
|
ARF = $(AR)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Convenience file list macros:
|
||||||
|
#
|
||||||
|
SOURCES = $(CXXFILES)
|
||||||
|
OBJECTS = $(CXXFILES:.cpp=.o)
|
||||||
|
|
||||||
|
#
|
||||||
|
# always unsatisfied target
|
||||||
|
#
|
||||||
|
_FORCE = $(COMMONPREF)_force
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# common rules
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# default target. makefiles must define a target named `targets'.
|
||||||
|
#
|
||||||
|
$(COMMONPREF)default: targets
|
||||||
|
|
||||||
|
#
|
||||||
|
# always unsatisfied target
|
||||||
|
#
|
||||||
|
$(_FORCE):
|
||||||
|
|
||||||
|
#
|
||||||
|
# cleaners
|
||||||
|
#
|
||||||
|
COMMONTARGETS = clean clobber
|
||||||
|
$(COMMONPREF)clean: $(_FORCE)
|
||||||
|
$(RM) $(DIRT)
|
||||||
|
|
||||||
|
$(COMMONPREF)clobber: clean $(_FORCE)
|
||||||
|
$(RM) $(TARGETS)
|
||||||
|
|
||||||
|
#
|
||||||
|
# implicit target rules
|
||||||
|
#
|
||||||
|
.SUFFIXES: .cpp .o
|
||||||
|
|
||||||
|
.cpp.o:
|
||||||
|
$(MKDEPPRE)
|
||||||
|
$(CXX) $(CXXFLAGS) -c $<
|
||||||
|
$(MKDEPPOST)
|
||||||
|
|
||||||
|
#
|
||||||
|
# platform stuff
|
||||||
|
#
|
||||||
|
include $(DEPTH)/Make-linux
|
||||||
|
#include $(DEPTH)/Make-solaris
|
||||||
|
|
||||||
|
#
|
||||||
|
# load dependencies
|
||||||
|
#
|
||||||
|
sinclude $(MKDEPFILE)
|
23
TMethodJob.h
23
TMethodJob.h
|
@ -1,23 +0,0 @@
|
||||||
#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
|
|
40
XBase.cpp
40
XBase.cpp
|
@ -1,40 +0,0 @@
|
||||||
#include "XBase.h"
|
|
||||||
|
|
||||||
// win32 wants a const char* argument to std::exception c'tor
|
|
||||||
#if CONFIG_PLATFORM_WIN32
|
|
||||||
#define STDEXCEPTARG ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// default to no argument
|
|
||||||
#ifndef STDEXCEPTARG
|
|
||||||
#define STDEXCEPTARG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//
|
|
||||||
// XBase
|
|
||||||
//
|
|
||||||
|
|
||||||
XBase::XBase() : exception(STDEXCEPTARG)
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
XBase::~XBase()
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* XBase::what() const
|
|
||||||
{
|
|
||||||
return getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* XBase::getType() const
|
|
||||||
{
|
|
||||||
return "XBase.h";
|
|
||||||
}
|
|
||||||
|
|
||||||
CString XBase::format(const CString& fmt) const
|
|
||||||
{
|
|
||||||
return fmt;
|
|
||||||
}
|
|
31
XBase.h
31
XBase.h
|
@ -1,31 +0,0 @@
|
||||||
#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
|
|
33
XSocket.h
33
XSocket.h
|
@ -1,33 +0,0 @@
|
||||||
#ifndef XSOCKET_H
|
|
||||||
#define XSOCKET_H
|
|
||||||
|
|
||||||
#include "XBase.h"
|
|
||||||
|
|
||||||
class XSocket : public XBase {
|
|
||||||
public:
|
|
||||||
// accessors
|
|
||||||
|
|
||||||
const char* getMessage() const { return m_msg; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
XSocket(const char* msg) : m_msg(msg) { }
|
|
||||||
|
|
||||||
private:
|
|
||||||
const char* m_msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define XSOCKETDEF(_n) \
|
|
||||||
class _n : public XSocket { \
|
|
||||||
public: \
|
|
||||||
_n(const char* msg) : XSocket(msg) { } \
|
|
||||||
XNAME(_n) \
|
|
||||||
};
|
|
||||||
|
|
||||||
XSOCKETDEF(XSocketCreate)
|
|
||||||
XSOCKETDEF(XSocketName)
|
|
||||||
XSOCKETDEF(XSocketConnect)
|
|
||||||
XSOCKETDEF(XSocketListen)
|
|
||||||
XSOCKETDEF(XSocketAccept)
|
|
||||||
XSOCKETDEF(XSocketWrite)
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef BASICTYPES_H
|
||||||
|
#define BASICTYPES_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_LINUX)
|
||||||
|
|
||||||
|
#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;
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_LINUX
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_SOLARIS)
|
||||||
|
|
||||||
|
#include <inttypes.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;
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_SOLARIS
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_WIN32
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
#include "CFunctionJob.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CFunctionJob
|
||||||
|
//
|
||||||
|
|
||||||
|
CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) :
|
||||||
|
m_func(func),
|
||||||
|
m_arg(arg)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFunctionJob::run()
|
||||||
|
{
|
||||||
|
if (m_func != NULL) {
|
||||||
|
m_func(m_arg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#ifndef CFUNCTIONJOB_H
|
||||||
|
#define CFUNCTIONJOB_H
|
||||||
|
|
||||||
|
#include "IJob.h"
|
||||||
|
|
||||||
|
class CFunctionJob : public IJob {
|
||||||
|
public:
|
||||||
|
CFunctionJob(void (*func)(void*), void* arg = NULL);
|
||||||
|
|
||||||
|
// IJob overrides
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void (*m_func)(void*);
|
||||||
|
void* m_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,180 @@
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CStopwatch
|
||||||
|
//
|
||||||
|
|
||||||
|
CStopwatch::CStopwatch(bool triggered) :
|
||||||
|
m_mark(0.0),
|
||||||
|
m_triggered(triggered),
|
||||||
|
m_stopped(triggered)
|
||||||
|
{
|
||||||
|
if (!triggered)
|
||||||
|
m_mark = getClock();
|
||||||
|
}
|
||||||
|
|
||||||
|
CStopwatch::~CStopwatch()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
double CStopwatch::reset()
|
||||||
|
{
|
||||||
|
if (m_stopped) {
|
||||||
|
const double dt = m_mark;
|
||||||
|
m_mark = 0.0;
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const double t = getClock();
|
||||||
|
const double dt = t - m_mark;
|
||||||
|
m_mark = t;
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStopwatch::stop()
|
||||||
|
{
|
||||||
|
if (m_stopped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// save the elapsed time
|
||||||
|
m_mark = getClock() - m_mark;
|
||||||
|
m_stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStopwatch::start()
|
||||||
|
{
|
||||||
|
m_triggered = false;
|
||||||
|
if (!m_stopped)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set the mark such that it reports the time elapsed at stop()
|
||||||
|
m_mark = getClock() - m_mark;
|
||||||
|
m_stopped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStopwatch::setTrigger()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
m_triggered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CStopwatch::getTime()
|
||||||
|
{
|
||||||
|
if (m_triggered) {
|
||||||
|
const double dt = m_mark;
|
||||||
|
start();
|
||||||
|
return dt;
|
||||||
|
}
|
||||||
|
if (m_stopped)
|
||||||
|
return m_mark;
|
||||||
|
return getClock() - m_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStopwatch::operator double()
|
||||||
|
{
|
||||||
|
return getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CStopwatch::isStopped() const
|
||||||
|
{
|
||||||
|
return m_stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
double CStopwatch::getTime() const
|
||||||
|
{
|
||||||
|
if (m_stopped)
|
||||||
|
return m_mark;
|
||||||
|
return getClock() - m_mark;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStopwatch::operator double() const
|
||||||
|
{
|
||||||
|
return getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_UNIX)
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
double CStopwatch::getClock() const
|
||||||
|
{
|
||||||
|
struct timeval t;
|
||||||
|
gettimeofday(&t, NULL);
|
||||||
|
return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_UNIX
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
|
||||||
|
// avoid getting a lot a crap from mmsystem.h that we don't need
|
||||||
|
#define MMNODRV // Installable driver support
|
||||||
|
#define MMNOSOUND // Sound support
|
||||||
|
#define MMNOWAVE // Waveform support
|
||||||
|
#define MMNOMIDI // MIDI support
|
||||||
|
#define MMNOAUX // Auxiliary audio support
|
||||||
|
#define MMNOMIXER // Mixer support
|
||||||
|
#define MMNOJOY // Joystick support
|
||||||
|
#define MMNOMCI // MCI support
|
||||||
|
#define MMNOMMIO // Multimedia file I/O support
|
||||||
|
#define MMNOMMSYSTEM // General MMSYSTEM functions
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <mmsystem.h>
|
||||||
|
|
||||||
|
typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void);
|
||||||
|
|
||||||
|
static double s_freq = 0.0;
|
||||||
|
static HINSTANCE s_mmInstance = NULL;
|
||||||
|
static PTimeGetTime s_tgt = NULL;
|
||||||
|
|
||||||
|
//
|
||||||
|
// initialize local variables
|
||||||
|
//
|
||||||
|
|
||||||
|
class CStopwatchInit {
|
||||||
|
public:
|
||||||
|
CStopwatchInit();
|
||||||
|
~CStopwatchInit();
|
||||||
|
};
|
||||||
|
static CStopwatchInit s_init;
|
||||||
|
|
||||||
|
CStopwatchInit::CStopwatchInit()
|
||||||
|
{
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) {
|
||||||
|
s_freq = 1.0 / static_cast<double>(freq.QuadPart);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// load winmm.dll and get timeGetTime
|
||||||
|
s_mmInstance = LoadLibrary("winmm");
|
||||||
|
if (s_mmInstance)
|
||||||
|
s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CStopwatchInit::~CStopwatchInit()
|
||||||
|
{
|
||||||
|
if (s_mmInstance)
|
||||||
|
FreeLibrary(reinterpret_cast<HMODULE>(s_mmInstance));
|
||||||
|
}
|
||||||
|
|
||||||
|
double CStopwatch::getClock() const
|
||||||
|
{
|
||||||
|
// get time. we try three ways, in order of descending precision
|
||||||
|
if (s_freq != 0.0) {
|
||||||
|
LARGE_INTEGER c;
|
||||||
|
QueryPerformanceCounter(&c);
|
||||||
|
return s_freq * static_cast<double>(c.QuadPart);
|
||||||
|
}
|
||||||
|
else if (s_tgt) {
|
||||||
|
return 0.001 * static_cast<double>(s_tgt());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0.001 * static_cast<double>(GetTickCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_WIN32
|
|
@ -0,0 +1,57 @@
|
||||||
|
#ifndef CSTOPWATCH_H
|
||||||
|
#define CSTOPWATCH_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class CStopwatch {
|
||||||
|
public:
|
||||||
|
// the default constructor does an implicit reset() or setTrigger().
|
||||||
|
// if triggered == false then the clock starts ticking.
|
||||||
|
CStopwatch(bool triggered = false);
|
||||||
|
~CStopwatch();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// set the start time to the current time, returning the time since
|
||||||
|
// the last reset. this does not remove the trigger if it's set nor
|
||||||
|
// does it start a stopped clock. if the clock is stopped then
|
||||||
|
// subsequent reset()'s will return 0.
|
||||||
|
double reset();
|
||||||
|
|
||||||
|
// stop and start the stopwatch. while stopped, no time elapses.
|
||||||
|
// stop() does not remove the trigger but start() does, even if
|
||||||
|
// the clock was already started.
|
||||||
|
void stop();
|
||||||
|
void start();
|
||||||
|
|
||||||
|
// setTrigger() stops the clock like stop() except there's an
|
||||||
|
// implicit start() the next time (non-const) getTime() is called.
|
||||||
|
// this is useful when you want the clock to start the first time
|
||||||
|
// you check it.
|
||||||
|
void setTrigger();
|
||||||
|
|
||||||
|
// return the time since the last reset() (or call reset() and
|
||||||
|
// return zero if the trigger is set).
|
||||||
|
double getTime();
|
||||||
|
operator double();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// returns true if the watch is stopped
|
||||||
|
bool isStopped() const;
|
||||||
|
|
||||||
|
// return the time since the last reset(). these cannot trigger
|
||||||
|
// the clock to start so if the trigger is set it's as if it wasn't.
|
||||||
|
double getTime() const;
|
||||||
|
operator double() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double getClock() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_mark;
|
||||||
|
bool m_triggered;
|
||||||
|
bool m_stopped;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef CSTRING_H
|
||||||
|
#define CSTRING_H
|
||||||
|
|
||||||
|
#include "common.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,11 @@
|
||||||
|
#ifndef IINTERFACE_H
|
||||||
|
#define IINTERFACE_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class IInterface {
|
||||||
|
public:
|
||||||
|
virtual ~IInterface() { }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,13 +1,10 @@
|
||||||
#ifndef IJOB_H
|
#ifndef IJOB_H
|
||||||
#define IJOB_H
|
#define IJOB_H
|
||||||
|
|
||||||
class IJob {
|
#include "IInterface.h"
|
||||||
|
|
||||||
|
class IJob : public IInterface {
|
||||||
public:
|
public:
|
||||||
IJob() { }
|
|
||||||
virtual ~IJob() { }
|
|
||||||
|
|
||||||
// manipulators
|
|
||||||
|
|
||||||
virtual void run() = 0;
|
virtual void run() = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
DEPTH=..
|
||||||
|
include $(DEPTH)/Makecommon
|
||||||
|
|
||||||
|
#
|
||||||
|
# target file
|
||||||
|
#
|
||||||
|
TARGET = base
|
||||||
|
|
||||||
|
#
|
||||||
|
# source files
|
||||||
|
#
|
||||||
|
LCXXINCS = \
|
||||||
|
$(NULL)
|
||||||
|
CXXFILES = \
|
||||||
|
XBase.cpp \
|
||||||
|
CFunctionJob.cpp \
|
||||||
|
CStopwatch.cpp \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
targets: $(LIBTARGET)
|
||||||
|
|
||||||
|
$(LIBTARGET): $(OBJECTS)
|
||||||
|
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
|
||||||
|
$(ARF) $(LIBTARGET) $(OBJECTS)
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef CMETHODJOB_H
|
||||||
|
#define CMETHODJOB_H
|
||||||
|
|
||||||
|
#include "IJob.h"
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class TMethodJob : public IJob {
|
||||||
|
public:
|
||||||
|
TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL);
|
||||||
|
|
||||||
|
// IJob overrides
|
||||||
|
virtual void run();
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_object;
|
||||||
|
void (T::*m_method)(void*);
|
||||||
|
void* m_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
TMethodJob<T>::TMethodJob(T* object, void (T::*method)(void*), void* arg) :
|
||||||
|
m_object(object),
|
||||||
|
m_method(method),
|
||||||
|
m_arg(arg)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
void TMethodJob<T>::run()
|
||||||
|
{
|
||||||
|
if (m_object != NULL) {
|
||||||
|
(m_object->*m_method)(m_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "XBase.h"
|
||||||
|
#include <errno.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), m_what()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
XBase::XBase(const CString& msg) : exception(STDEXCEPTARG), m_what(msg)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
XBase::~XBase()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* XBase::what() const
|
||||||
|
{
|
||||||
|
if (m_what.empty()) {
|
||||||
|
m_what = getWhat();
|
||||||
|
}
|
||||||
|
return m_what.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
CString XBase::format(const char* /*id*/,
|
||||||
|
const char* fmt, ...) const throw()
|
||||||
|
{
|
||||||
|
// FIXME -- use id to lookup formating string
|
||||||
|
// FIXME -- format string with arguments
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MXErrno
|
||||||
|
//
|
||||||
|
|
||||||
|
MXErrno::MXErrno() : m_errno(errno)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
MXErrno::MXErrno(int err) : m_errno(err)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
int MXErrno::getErrno() const
|
||||||
|
{
|
||||||
|
return m_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* MXErrno::getErrstr() const
|
||||||
|
{
|
||||||
|
return strerror(m_errno);
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef XBASE_H
|
||||||
|
#define XBASE_H
|
||||||
|
|
||||||
|
#include "CString.h"
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
class XBase : public std::exception {
|
||||||
|
public:
|
||||||
|
XBase();
|
||||||
|
XBase(const CString& msg);
|
||||||
|
virtual ~XBase();
|
||||||
|
|
||||||
|
// std::exception overrides
|
||||||
|
virtual const char* what() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// returns a human readable string describing the exception
|
||||||
|
virtual CString getWhat() const throw() = 0;
|
||||||
|
|
||||||
|
// look up a message and format it
|
||||||
|
virtual CString format(const char* id,
|
||||||
|
const char* defaultFormat, ...) const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable CString m_what;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MXErrno {
|
||||||
|
public:
|
||||||
|
MXErrno();
|
||||||
|
MXErrno(int);
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
int getErrno() const;
|
||||||
|
const char* getErrstr() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_errno;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef COMMON_H
|
||||||
|
#define COMMON_H
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
|
||||||
|
#define CONFIG_PLATFORM_LINUX
|
||||||
|
#define CONFIG_PLATFORM_UNIX
|
||||||
|
#define CONFIG_TYPES_X11
|
||||||
|
#define CONFIG_PTHREADS
|
||||||
|
|
||||||
|
#elif defined(__sun__)
|
||||||
|
|
||||||
|
#define CONFIG_PLATFORM_SOLARIS
|
||||||
|
#define CONFIG_PLATFORM_UNIX
|
||||||
|
#define CONFIG_TYPES_X11
|
||||||
|
#define CONFIG_PTHREADS
|
||||||
|
|
||||||
|
#elif defined(_WINDOWS) && defined(WIN32)
|
||||||
|
|
||||||
|
#define CONFIG_PLATFORM_WIN32
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#error unsupported platform
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NULL
|
||||||
|
#define NULL 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include "CBufferedInputStream.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "IJob.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CBufferedInputStream
|
||||||
|
//
|
||||||
|
|
||||||
|
CBufferedInputStream::CBufferedInputStream(CMutex* mutex, IJob* closeCB) :
|
||||||
|
m_mutex(mutex),
|
||||||
|
m_empty(mutex, true),
|
||||||
|
m_closeCB(closeCB),
|
||||||
|
m_closed(false),
|
||||||
|
m_hungup(false)
|
||||||
|
{
|
||||||
|
assert(m_mutex != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBufferedInputStream::~CBufferedInputStream()
|
||||||
|
{
|
||||||
|
delete m_closeCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedInputStream::write(
|
||||||
|
const void* data, UInt32 n) throw()
|
||||||
|
{
|
||||||
|
if (!m_hungup && n > 0) {
|
||||||
|
m_buffer.write(data, n);
|
||||||
|
m_empty = (m_buffer.getSize() == 0);
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedInputStream::hangup() throw()
|
||||||
|
{
|
||||||
|
m_hungup = true;
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedInputStream::readNoLock(
|
||||||
|
void* dst, UInt32 n) throw(XIO)
|
||||||
|
{
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for data (or hangup)
|
||||||
|
while (!m_hungup && m_empty == true) {
|
||||||
|
m_empty.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// read data
|
||||||
|
const UInt32 count = m_buffer.getSize();
|
||||||
|
if (n > count) {
|
||||||
|
n = count;
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
|
if (dst != NULL) {
|
||||||
|
::memcpy(dst, m_buffer.peek(n), n);
|
||||||
|
}
|
||||||
|
m_buffer.pop(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update empty state
|
||||||
|
if (m_buffer.getSize() == 0) {
|
||||||
|
m_empty = true;
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedInputStream::getSizeNoLock() const throw()
|
||||||
|
{
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedInputStream::close() throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_closed = true;
|
||||||
|
if (m_closeCB) {
|
||||||
|
m_closeCB->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedInputStream::read(
|
||||||
|
void* dst, UInt32 n) throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
return readNoLock(dst, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedInputStream::getSize() const throw()
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
return getSizeNoLock();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
#ifndef CBUFFEREDINPUTSTREAM_H
|
||||||
|
#define CBUFFEREDINPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "CStreamBuffer.h"
|
||||||
|
#include "CCondVar.h"
|
||||||
|
#include "IInputStream.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class IJob;
|
||||||
|
|
||||||
|
class CBufferedInputStream : public IInputStream {
|
||||||
|
public:
|
||||||
|
CBufferedInputStream(CMutex*, IJob* adoptedCloseCB);
|
||||||
|
~CBufferedInputStream();
|
||||||
|
|
||||||
|
// the caller is expected to lock the mutex before calling
|
||||||
|
// methods unless otherwise noted.
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// write() appends n bytes to the buffer
|
||||||
|
void write(const void*, UInt32 n) throw();
|
||||||
|
|
||||||
|
// causes read() to always return immediately. if there is no
|
||||||
|
// more data then it returns 0. further writes are discarded.
|
||||||
|
void hangup() throw();
|
||||||
|
|
||||||
|
// same as read() but caller must lock the mutex
|
||||||
|
UInt32 readNoLock(void*, UInt32 count) throw(XIO);
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// same as getSize() but caller must lock the mutex
|
||||||
|
UInt32 getSizeNoLock() const throw();
|
||||||
|
|
||||||
|
// IInputStream overrides
|
||||||
|
// these all lock the mutex for their duration
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
virtual UInt32 read(void*, UInt32 count) throw(XIO);
|
||||||
|
virtual UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex* m_mutex;
|
||||||
|
CCondVar<bool> m_empty;
|
||||||
|
IJob* m_closeCB;
|
||||||
|
CStreamBuffer m_buffer;
|
||||||
|
bool m_closed;
|
||||||
|
bool m_hungup;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include "CBufferedOutputStream.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "IJob.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CBufferedOutputStream
|
||||||
|
//
|
||||||
|
|
||||||
|
CBufferedOutputStream::CBufferedOutputStream(CMutex* mutex, IJob* closeCB) :
|
||||||
|
m_mutex(mutex),
|
||||||
|
m_closeCB(closeCB),
|
||||||
|
m_closed(false)
|
||||||
|
{
|
||||||
|
assert(m_mutex != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBufferedOutputStream::~CBufferedOutputStream()
|
||||||
|
{
|
||||||
|
delete m_closeCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* CBufferedOutputStream::peek(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
return m_buffer.peek(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedOutputStream::pop(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
m_buffer.pop(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedOutputStream::getSize() const throw()
|
||||||
|
{
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedOutputStream::close() throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_closed = true;
|
||||||
|
if (m_closeCB) {
|
||||||
|
m_closeCB->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedOutputStream::write(
|
||||||
|
const void* data, UInt32 n) throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer.write(data, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBufferedOutputStream::flush() throw(XIO)
|
||||||
|
{
|
||||||
|
// wait until all data is written
|
||||||
|
while (getSizeWithLock() > 0) {
|
||||||
|
CThread::sleep(0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CBufferedOutputStream::getSizeWithLock() const throw()
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef CBUFFEREDOUTPUTSTREAM_H
|
||||||
|
#define CBUFFEREDOUTPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "CStreamBuffer.h"
|
||||||
|
#include "IOutputStream.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class IJob;
|
||||||
|
|
||||||
|
class CBufferedOutputStream : public IOutputStream {
|
||||||
|
public:
|
||||||
|
CBufferedOutputStream(CMutex*, IJob* adoptedCloseCB);
|
||||||
|
~CBufferedOutputStream();
|
||||||
|
|
||||||
|
// the caller is expected to lock the mutex before calling
|
||||||
|
// methods unless otherwise noted.
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||||
|
// pop() discards the next n bytes.
|
||||||
|
const void* peek(UInt32 n) throw();
|
||||||
|
void pop(UInt32 n) throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// return the number of bytes in the buffer
|
||||||
|
UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
// IOutputStream overrides
|
||||||
|
// these all lock the mutex for their duration
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
virtual UInt32 write(const void*, UInt32 count) throw(XIO);
|
||||||
|
virtual void flush() throw(XIO);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UInt32 getSizeWithLock() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex* m_mutex;
|
||||||
|
IJob* m_closeCB;
|
||||||
|
CStreamBuffer m_buffer;
|
||||||
|
bool m_closed;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "CInputStreamFilter.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CInputStreamFilter
|
||||||
|
//
|
||||||
|
|
||||||
|
CInputStreamFilter::CInputStreamFilter(IInputStream* stream, bool adopted) :
|
||||||
|
m_stream(stream),
|
||||||
|
m_adopted(adopted)
|
||||||
|
{
|
||||||
|
assert(m_stream != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CInputStreamFilter::~CInputStreamFilter()
|
||||||
|
{
|
||||||
|
if (m_adopted) {
|
||||||
|
delete m_stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IInputStream* CInputStreamFilter::getStream() const throw()
|
||||||
|
{
|
||||||
|
return m_stream;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef CINPUTSTREAMFILTER_H
|
||||||
|
#define CINPUTSTREAMFILTER_H
|
||||||
|
|
||||||
|
#include "IInputStream.h"
|
||||||
|
|
||||||
|
class CInputStreamFilter : public IInputStream {
|
||||||
|
public:
|
||||||
|
CInputStreamFilter(IInputStream*, bool adoptStream = true);
|
||||||
|
~CInputStreamFilter();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// IInputStream overrides
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
virtual UInt32 read(void*, UInt32 maxCount) throw(XIO) = 0;
|
||||||
|
virtual UInt32 getSize() const throw() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IInputStream* getStream() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IInputStream* m_stream;
|
||||||
|
bool m_adopted;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "COutputStreamFilter.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// COutputStreamFilter
|
||||||
|
//
|
||||||
|
|
||||||
|
COutputStreamFilter::COutputStreamFilter(IOutputStream* stream, bool adopted) :
|
||||||
|
m_stream(stream),
|
||||||
|
m_adopted(adopted)
|
||||||
|
{
|
||||||
|
assert(m_stream != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
COutputStreamFilter::~COutputStreamFilter()
|
||||||
|
{
|
||||||
|
if (m_adopted) {
|
||||||
|
delete m_stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IOutputStream* COutputStreamFilter::getStream() const throw()
|
||||||
|
{
|
||||||
|
return m_stream;
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef COUTPUTSTREAMFILTER_H
|
||||||
|
#define COUTPUTSTREAMFILTER_H
|
||||||
|
|
||||||
|
#include "IOutputStream.h"
|
||||||
|
|
||||||
|
class COutputStreamFilter : public IOutputStream {
|
||||||
|
public:
|
||||||
|
COutputStreamFilter(IOutputStream*, bool adoptStream = true);
|
||||||
|
~COutputStreamFilter();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// IOutputStream overrides
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0;
|
||||||
|
virtual void flush() throw(XIO) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IOutputStream* getStream() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
IOutputStream* m_stream;
|
||||||
|
bool m_adopted;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,107 @@
|
||||||
|
#include "CStreamBuffer.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CStreamBuffer
|
||||||
|
//
|
||||||
|
|
||||||
|
const UInt32 CStreamBuffer::kChunkSize = 4096;
|
||||||
|
|
||||||
|
CStreamBuffer::CStreamBuffer() : m_size(0)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
CStreamBuffer::~CStreamBuffer()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* CStreamBuffer::peek(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
assert(n <= m_size);
|
||||||
|
|
||||||
|
// reserve space in first chunk
|
||||||
|
ChunkList::iterator head = m_chunks.begin();
|
||||||
|
head->reserve(n);
|
||||||
|
|
||||||
|
// consolidate chunks into the first chunk until it has n bytes
|
||||||
|
ChunkList::iterator scan = head;
|
||||||
|
++scan;
|
||||||
|
while (head->size() < n && scan != m_chunks.end()) {
|
||||||
|
head->insert(head->end(), scan->begin(), scan->end());
|
||||||
|
scan = m_chunks.erase(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<const void*>(head->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStreamBuffer::pop(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
m_size -= n;
|
||||||
|
|
||||||
|
// discard chunks until more than n bytes would've been discarded
|
||||||
|
ChunkList::iterator scan = m_chunks.begin();
|
||||||
|
while (scan->size() <= n && scan != m_chunks.end()) {
|
||||||
|
n -= scan->size();
|
||||||
|
scan = m_chunks.erase(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's anything left over then remove it from the head chunk.
|
||||||
|
// if there's no head chunk then we're already empty.
|
||||||
|
if (scan == m_chunks.end()) {
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
else if (n > 0) {
|
||||||
|
scan->erase(scan->begin(), scan->begin() + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStreamBuffer::write(
|
||||||
|
const void* vdata, UInt32 n) throw()
|
||||||
|
{
|
||||||
|
assert(vdata != NULL);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_size += n;
|
||||||
|
|
||||||
|
// cast data to bytes
|
||||||
|
const UInt8* data = reinterpret_cast<const UInt8*>(vdata);
|
||||||
|
|
||||||
|
// point to last chunk if it has space, otherwise append an empty chunk
|
||||||
|
ChunkList::iterator scan = m_chunks.end();
|
||||||
|
if (scan != m_chunks.begin()) {
|
||||||
|
--scan;
|
||||||
|
if (scan->size() >= kChunkSize)
|
||||||
|
++scan;
|
||||||
|
}
|
||||||
|
if (scan == m_chunks.end()) {
|
||||||
|
scan = m_chunks.insert(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append data in chunks
|
||||||
|
while (n > 0) {
|
||||||
|
// choose number of bytes for next chunk
|
||||||
|
UInt32 count = kChunkSize - scan->size();
|
||||||
|
if (count > n)
|
||||||
|
count = n;
|
||||||
|
|
||||||
|
// transfer data
|
||||||
|
scan->insert(scan->end(), data, data + count);
|
||||||
|
n -= count;
|
||||||
|
data += count;
|
||||||
|
|
||||||
|
// append another empty chunk if we're not done yet
|
||||||
|
if (n > 0) {
|
||||||
|
scan = m_chunks.insert(scan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CStreamBuffer::getSize() const throw()
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef CSTREAMBUFFER_H
|
||||||
|
#define CSTREAMBUFFER_H
|
||||||
|
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CStreamBuffer {
|
||||||
|
public:
|
||||||
|
CStreamBuffer();
|
||||||
|
~CStreamBuffer();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||||
|
// pop() discards the next n bytes.
|
||||||
|
const void* peek(UInt32 n) throw();
|
||||||
|
void pop(UInt32 n) throw();
|
||||||
|
|
||||||
|
// write() appends n bytes to the buffer
|
||||||
|
void write(const void*, UInt32 n) throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// return the number of bytes in the buffer
|
||||||
|
UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const UInt32 kChunkSize;
|
||||||
|
|
||||||
|
typedef std::vector<UInt8> Chunk;
|
||||||
|
typedef std::list<Chunk> ChunkList;
|
||||||
|
|
||||||
|
ChunkList m_chunks;
|
||||||
|
UInt32 m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef IINPUTSTREAM_H
|
||||||
|
#define IINPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
|
||||||
|
class IInputStream : public IInterface {
|
||||||
|
public:
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// close the stream
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
|
||||||
|
// read up to maxCount bytes into buffer, return number read.
|
||||||
|
// blocks if no data is currently available. if buffer is NULL
|
||||||
|
// then the data is discarded.
|
||||||
|
virtual UInt32 read(void* buffer, UInt32 maxCount) throw(XIO) = 0;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// get a conservative estimate of the available bytes to read
|
||||||
|
// (i.e. a number not greater than the actual number of bytes).
|
||||||
|
// some streams may not be able to determine this and will always
|
||||||
|
// return zero.
|
||||||
|
virtual UInt32 getSize() const throw() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef IOUTPUTSTREAM_H
|
||||||
|
#define IOUTPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
|
||||||
|
class IOutputStream : public IInterface {
|
||||||
|
public:
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// close the stream
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
|
||||||
|
// write count bytes to stream
|
||||||
|
virtual UInt32 write(const void*, UInt32 count) throw(XIO) = 0;
|
||||||
|
|
||||||
|
// flush the stream
|
||||||
|
virtual void flush() throw(XIO) = 0;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,29 @@
|
||||||
|
DEPTH=..
|
||||||
|
include $(DEPTH)/Makecommon
|
||||||
|
|
||||||
|
#
|
||||||
|
# target file
|
||||||
|
#
|
||||||
|
TARGET = io
|
||||||
|
|
||||||
|
#
|
||||||
|
# source files
|
||||||
|
#
|
||||||
|
LCXXINCS = \
|
||||||
|
-I$(DEPTH)/base \
|
||||||
|
-I$(DEPTH)/mt \
|
||||||
|
$(NULL)
|
||||||
|
CXXFILES = \
|
||||||
|
XIO.cpp \
|
||||||
|
CInputStreamFilter.cpp \
|
||||||
|
COutputStreamFilter.cpp \
|
||||||
|
CStreamBuffer.cpp \
|
||||||
|
CBufferedInputStream.cpp \
|
||||||
|
CBufferedOutputStream.cpp \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
targets: $(LIBTARGET)
|
||||||
|
|
||||||
|
$(LIBTARGET): $(OBJECTS) $(DEPLIBS)
|
||||||
|
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
|
||||||
|
$(ARF) $(LIBTARGET) $(OBJECTS)
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include "XIO.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// XIOErrno
|
||||||
|
//
|
||||||
|
|
||||||
|
XIOErrno::XIOErrno() : MXErrno()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
XIOErrno::XIOErrno(int err) : MXErrno(err)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XIOClose
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XIOClose::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XIOClose", "close: %1", XIOErrno::getErrstr());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XIOClosed
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XIOClosed::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XIOClosed", "already closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XIOEndOfStream
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XIOEndOfStream::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XIOEndOfStream", "reached end of stream");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef XIO_H
|
||||||
|
#define XIO_H
|
||||||
|
|
||||||
|
#include "XBase.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
class XIO : public XBase { };
|
||||||
|
|
||||||
|
class XIOErrno : public XIO, public MXErrno {
|
||||||
|
public:
|
||||||
|
XIOErrno();
|
||||||
|
XIOErrno(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
class XIOClose: public XIOErrno {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
class XIOClosed : public XIO {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
class XIOEndOfStream : public XIO {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
114
main.cpp
114
main.cpp
|
@ -1,114 +0,0 @@
|
||||||
#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,296 @@
|
||||||
|
#include "CCondVar.h"
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CCondVarBase
|
||||||
|
//
|
||||||
|
|
||||||
|
CCondVarBase::CCondVarBase(CMutex* mutex) :
|
||||||
|
m_mutex(mutex)
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
, m_waitCountMutex()
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
assert(m_mutex != NULL);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CCondVarBase::~CCondVarBase()
|
||||||
|
{
|
||||||
|
fini();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::lock() const throw()
|
||||||
|
{
|
||||||
|
m_mutex->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::unlock() const throw()
|
||||||
|
{
|
||||||
|
m_mutex->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCondVarBase::wait(double timeout) const
|
||||||
|
{
|
||||||
|
CStopwatch timer(true);
|
||||||
|
return wait(timer, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
CMutex* CCondVarBase::getMutex() const throw()
|
||||||
|
{
|
||||||
|
return m_mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
|
||||||
|
#include "CThread.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
void CCondVarBase::init()
|
||||||
|
{
|
||||||
|
pthread_cond_t* cond = new pthread_cond_t;
|
||||||
|
int status = pthread_cond_init(cond, NULL);
|
||||||
|
assert(status == 0);
|
||||||
|
m_cond = reinterpret_cast<pthread_cond_t*>(cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::fini()
|
||||||
|
{
|
||||||
|
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||||
|
int status = pthread_cond_destroy(cond);
|
||||||
|
assert(status == 0);
|
||||||
|
delete cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::signal() throw()
|
||||||
|
{
|
||||||
|
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||||
|
int status = pthread_cond_signal(cond);
|
||||||
|
assert(status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::broadcast() throw()
|
||||||
|
{
|
||||||
|
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||||
|
int status = pthread_cond_broadcast(cond);
|
||||||
|
assert(status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCondVarBase::wait(
|
||||||
|
CStopwatch& timer, double timeout) const
|
||||||
|
{
|
||||||
|
// check timeout against timer
|
||||||
|
if (timeout >= 0.0) {
|
||||||
|
timeout -= timer.getTime();
|
||||||
|
if (timeout < 0.0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get condition variable and mutex
|
||||||
|
pthread_cond_t* cond = reinterpret_cast<pthread_cond_t*>(m_cond);
|
||||||
|
pthread_mutex_t* mutex = reinterpret_cast<pthread_mutex_t*>(m_mutex->m_mutex);
|
||||||
|
|
||||||
|
// get final time
|
||||||
|
struct timeval now;
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
struct timespec finalTime;
|
||||||
|
finalTime.tv_sec = now.tv_sec;
|
||||||
|
finalTime.tv_nsec = now.tv_usec * 1000;
|
||||||
|
if (timeout >= 0.0) {
|
||||||
|
const long timeout_sec = (long)timeout;
|
||||||
|
const long timeout_nsec = (long)(1000000000.0 * (timeout - timeout_sec));
|
||||||
|
finalTime.tv_sec += timeout_sec;
|
||||||
|
finalTime.tv_nsec += timeout_nsec;
|
||||||
|
if (finalTime.tv_nsec >= 1000000000) {
|
||||||
|
finalTime.tv_nsec -= 1000000000;
|
||||||
|
finalTime.tv_sec += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeat until we reach the final time
|
||||||
|
int status;
|
||||||
|
for (;;) {
|
||||||
|
// compute the next timeout
|
||||||
|
gettimeofday(&now, NULL);
|
||||||
|
struct timespec endTime;
|
||||||
|
endTime.tv_sec = now.tv_sec;
|
||||||
|
endTime.tv_nsec = now.tv_usec * 1000 + 50000000;
|
||||||
|
if (endTime.tv_nsec >= 1000000000) {
|
||||||
|
endTime.tv_nsec -= 1000000000;
|
||||||
|
endTime.tv_sec += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if we should cancel this thread
|
||||||
|
CThread::testCancel();
|
||||||
|
|
||||||
|
// done if past final timeout
|
||||||
|
if (timeout >= 0.0) {
|
||||||
|
if (endTime.tv_sec > finalTime.tv_sec ||
|
||||||
|
(endTime.tv_sec == finalTime.tv_sec &&
|
||||||
|
endTime.tv_nsec >= finalTime.tv_nsec)) {
|
||||||
|
status = ETIMEDOUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait
|
||||||
|
status = pthread_cond_timedwait(cond, mutex, &endTime);
|
||||||
|
|
||||||
|
// check for cancel again
|
||||||
|
CThread::testCancel();
|
||||||
|
|
||||||
|
// check wait status
|
||||||
|
if (status != ETIMEDOUT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
// success
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case ETIMEDOUT:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0 && "condition variable wait error");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PTHREADS
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CThreadRep.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// note -- implementation taken from
|
||||||
|
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
|
||||||
|
// titled "Strategies for Implementing POSIX Condition Variables
|
||||||
|
// on Win32." it also provides an implementation that doesn't
|
||||||
|
// suffer from the incorrectness problem described in our
|
||||||
|
// corresponding header but it is slower, still unfair, and
|
||||||
|
// can cause busy waiting.
|
||||||
|
//
|
||||||
|
|
||||||
|
void CCondVarBase::init()
|
||||||
|
{
|
||||||
|
// prepare events
|
||||||
|
HANDLE* events = new HANDLE[2];
|
||||||
|
events[kSignal] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
events[kBroadcast] = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
|
||||||
|
// prepare members
|
||||||
|
m_cond = reinterpret_cast<void*>(events);
|
||||||
|
m_waitCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::fini()
|
||||||
|
{
|
||||||
|
HANDLE* events = reinterpret_cast<HANDLE*>(m_cond);
|
||||||
|
CloseHandle(events[kSignal]);
|
||||||
|
CloseHandle(events[kBroadcast]);
|
||||||
|
delete[] events;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::signal() throw()
|
||||||
|
{
|
||||||
|
// is anybody waiting?
|
||||||
|
bool hasWaiter;
|
||||||
|
{
|
||||||
|
CLock lock(&m_waitCountMutex);
|
||||||
|
hasWaiter = (m_waitCount > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake one thread if anybody is waiting
|
||||||
|
if (hasWaiter)
|
||||||
|
SetEvent(reinterpret_cast<HANDLE*>(m_cond)[kSignal]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CCondVarBase::broadcast() throw()
|
||||||
|
{
|
||||||
|
// is anybody waiting?
|
||||||
|
bool hasWaiter;
|
||||||
|
{
|
||||||
|
CLock lock(&m_waitCountMutex);
|
||||||
|
hasWaiter = (m_waitCount > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wake all threads if anybody is waiting
|
||||||
|
if (hasWaiter)
|
||||||
|
SetEvent(reinterpret_cast<HANDLE*>(m_cond)[kBroadcast]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CCondVarBase::wait(
|
||||||
|
CStopwatch& timer, double timeout) const
|
||||||
|
{
|
||||||
|
// check timeout against timer
|
||||||
|
if (timeout >= 0.0) {
|
||||||
|
timeout -= timer.getTime();
|
||||||
|
if (timeout < 0.0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare to wait
|
||||||
|
CRefCountedPtr<CThreadRep> currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
|
||||||
|
static_cast<DWORD>(1000.0 * timeout);
|
||||||
|
HANDLE* events = reinterpret_cast<HANDLE*>(m_cond);
|
||||||
|
HANDLE handles[3];
|
||||||
|
handles[0] = events[kSignal];
|
||||||
|
handles[1] = events[kBroadcast];
|
||||||
|
handles[2] = currentRep->getCancelEvent();
|
||||||
|
const DWORD n = currentRep->isCancellable() ? 3 : 2;
|
||||||
|
|
||||||
|
// update waiter count
|
||||||
|
{
|
||||||
|
CLock lock(&m_waitCountMutex);
|
||||||
|
++m_waitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// release mutex. this should be atomic with the wait so that it's
|
||||||
|
// impossible for another thread to signal us between the unlock and
|
||||||
|
// the wait, which would lead to a lost signal on broadcasts.
|
||||||
|
// however, we're using a manual reset event for broadcasts which
|
||||||
|
// stays set until we reset it, so we don't lose the broadcast.
|
||||||
|
m_mutex->unlock();
|
||||||
|
|
||||||
|
// wait for a signal or broadcast
|
||||||
|
DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, winTimeout);
|
||||||
|
|
||||||
|
// cancel takes priority
|
||||||
|
if (n == 3 && result != WAIT_OBJECT_0 + 2 &&
|
||||||
|
::WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0)
|
||||||
|
result = WAIT_OBJECT_0 + 2;
|
||||||
|
|
||||||
|
// update the waiter count and check if we're the last waiter
|
||||||
|
bool last;
|
||||||
|
{
|
||||||
|
CLock lock(&m_waitCountMutex);
|
||||||
|
--m_waitCount;
|
||||||
|
last = (result == WAIT_OBJECT_0 + 1 && m_waitCount == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the broadcast event if we're the last waiter
|
||||||
|
if (last)
|
||||||
|
ResetEvent(events[kBroadcast]);
|
||||||
|
|
||||||
|
// reacquire the mutex
|
||||||
|
m_mutex->lock();
|
||||||
|
|
||||||
|
// cancel thread if necessary
|
||||||
|
if (result == WAIT_OBJECT_0 + 2)
|
||||||
|
currentRep->testCancel();
|
||||||
|
|
||||||
|
// return success or failure
|
||||||
|
return (result == WAIT_OBJECT_0 + 0 ||
|
||||||
|
result == WAIT_OBJECT_0 + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_WIN32
|
|
@ -0,0 +1,144 @@
|
||||||
|
#ifndef CCONDVAR_H
|
||||||
|
#define CCONDVAR_H
|
||||||
|
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
class CStopwatch;
|
||||||
|
|
||||||
|
class CCondVarBase {
|
||||||
|
public:
|
||||||
|
// mutex must be supplied. all condition variables have an
|
||||||
|
// associated mutex. the copy c'tor uses the same mutex as the
|
||||||
|
// argument and is otherwise like the default c'tor.
|
||||||
|
CCondVarBase(CMutex* mutex);
|
||||||
|
~CCondVarBase();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// lock/unlock the mutex. see CMutex.
|
||||||
|
void lock() const throw();
|
||||||
|
void unlock() const throw();
|
||||||
|
|
||||||
|
// signal the condition. Signal() wakes one waiting thread.
|
||||||
|
// Broadcast() wakes all waiting threads.
|
||||||
|
void signal() throw();
|
||||||
|
void broadcast() throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// wait on the condition. if timeout < 0 then wait until signalled,
|
||||||
|
// otherwise up to timeout seconds or until signalled, whichever
|
||||||
|
// comes first. since clients normally wait on condition variables
|
||||||
|
// in a loop, clients can provide a CStopwatch that acts as the
|
||||||
|
// timeout clock. using it, clients don't have to recalculate the
|
||||||
|
// timeout on each iteration. passing a stopwatch with a negative
|
||||||
|
// timeout is pointless but permitted.
|
||||||
|
//
|
||||||
|
// returns true if the object was signalled during the wait, false
|
||||||
|
// otherwise.
|
||||||
|
//
|
||||||
|
// (cancellation point)
|
||||||
|
bool wait(double timeout = -1.0) const;
|
||||||
|
bool wait(CStopwatch&, double timeout) const;
|
||||||
|
|
||||||
|
// get the mutex passed to the c'tor
|
||||||
|
CMutex* getMutex() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
void fini();
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
CCondVarBase(const CCondVarBase&);
|
||||||
|
CCondVarBase& operator=(const CCondVarBase&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex* m_mutex;
|
||||||
|
void* m_cond;
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
enum { kSignal, kBroadcast };
|
||||||
|
mutable UInt32 m_waitCount;
|
||||||
|
CMutex m_waitCountMutex;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class CCondVar : public CCondVarBase {
|
||||||
|
public:
|
||||||
|
CCondVar(CMutex* mutex, const T&);
|
||||||
|
CCondVar(const CCondVar&);
|
||||||
|
~CCondVar();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// assigns the value of the variable
|
||||||
|
CCondVar& operator=(const CCondVar&);
|
||||||
|
|
||||||
|
// assign the value
|
||||||
|
CCondVar& operator=(const T&);
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// get the const value. this object should be locked before
|
||||||
|
// calling this method.
|
||||||
|
operator const T&() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>::CCondVar(CMutex* mutex, const T& data) :
|
||||||
|
CCondVarBase(mutex), m_data(data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>::CCondVar(const CCondVar& cv) :
|
||||||
|
CCondVarBase(cv.getMutex()),
|
||||||
|
m_data(cv.m_data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>::~CCondVar()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>& CCondVar<T>::operator=(const CCondVar<T>& cv)
|
||||||
|
{
|
||||||
|
m_data = cv.m_data;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>& CCondVar<T>::operator=(const T& data)
|
||||||
|
{
|
||||||
|
m_data = data;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
CCondVar<T>::operator const T&() const throw()
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// force instantiation of these common types
|
||||||
|
template class CCondVar<bool>;
|
||||||
|
template class CCondVar<SInt32>;
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CCondVar.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CLock
|
||||||
|
//
|
||||||
|
|
||||||
|
CLock::CLock(const CMutex* mutex) throw() : m_mutex(mutex)
|
||||||
|
{
|
||||||
|
m_mutex->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
CLock::CLock(const CCondVarBase* cv) throw() : m_mutex(cv->getMutex())
|
||||||
|
{
|
||||||
|
m_mutex->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
CLock::~CLock() throw()
|
||||||
|
{
|
||||||
|
m_mutex->unlock();
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CLOCK_H
|
||||||
|
#define CLOCK_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class CCondVarBase;
|
||||||
|
|
||||||
|
class CLock {
|
||||||
|
public:
|
||||||
|
CLock(const CMutex* mutex) throw();
|
||||||
|
CLock(const CCondVarBase* cv) throw();
|
||||||
|
~CLock() throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// not implemented
|
||||||
|
CLock(const CLock&);
|
||||||
|
CLock& operator=(const CLock&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CMutex* m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CMutex
|
||||||
|
//
|
||||||
|
|
||||||
|
CMutex::CMutex()
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMutex::CMutex(const CMutex&)
|
||||||
|
{
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMutex::~CMutex()
|
||||||
|
{
|
||||||
|
fini();
|
||||||
|
}
|
||||||
|
|
||||||
|
CMutex& CMutex::operator=(const CMutex&)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
void CMutex::init()
|
||||||
|
{
|
||||||
|
pthread_mutex_t* mutex = new pthread_mutex_t;
|
||||||
|
int status = pthread_mutex_init(mutex, NULL);
|
||||||
|
assert(status == 0);
|
||||||
|
// status = pthread_mutexattr_settype(mutex, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
// assert(status == 0);
|
||||||
|
m_mutex = reinterpret_cast<void*>(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::fini()
|
||||||
|
{
|
||||||
|
pthread_mutex_t* mutex = reinterpret_cast<pthread_mutex_t*>(m_mutex);
|
||||||
|
int status = pthread_mutex_destroy(mutex);
|
||||||
|
assert(status == 0);
|
||||||
|
delete mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::lock() const throw()
|
||||||
|
{
|
||||||
|
pthread_mutex_t* mutex = reinterpret_cast<pthread_mutex_t*>(m_mutex);
|
||||||
|
int status = pthread_mutex_lock(mutex);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
// success
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EDEADLK:
|
||||||
|
assert(0 && "lock already owned");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EAGAIN:
|
||||||
|
assert(0 && "too many recursive locks");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0 && "unexpected error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::unlock() const throw()
|
||||||
|
{
|
||||||
|
pthread_mutex_t* mutex = reinterpret_cast<pthread_mutex_t*>(m_mutex);
|
||||||
|
int status = pthread_mutex_unlock(mutex);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 0:
|
||||||
|
// success
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EPERM:
|
||||||
|
assert(0 && "thread doesn't own a lock");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(0 && "unexpected error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PTHREADS
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
void CMutex::init()
|
||||||
|
{
|
||||||
|
CRITICAL_SECTION* mutex = new CRITICAL_SECTION;
|
||||||
|
::InitializeCriticalSection(mutex);
|
||||||
|
m_mutex = reinterpret_cast<void*>(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::fini()
|
||||||
|
{
|
||||||
|
CRITICAL_SECTION* mutex = reinterpret_cast<CRITICAL_SECTION*>(m_mutex);
|
||||||
|
::DeleteCriticalSection(mutex);
|
||||||
|
delete mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::lock() const throw()
|
||||||
|
{
|
||||||
|
::EnterCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(m_mutex));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMutex::unlock() const throw()
|
||||||
|
{
|
||||||
|
::LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(m_mutex));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_PLATFORM_WIN32
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef CMUTEX_H
|
||||||
|
#define CMUTEX_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
// recursive mutex class
|
||||||
|
class CMutex {
|
||||||
|
public:
|
||||||
|
// copy c'tor is equivalent to default c'tor. it's here to
|
||||||
|
// allow copying of objects that have mutexes.
|
||||||
|
CMutex();
|
||||||
|
CMutex(const CMutex&);
|
||||||
|
~CMutex();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// this has no effect. it's only here to allow assignment of
|
||||||
|
// objects that have mutexes.
|
||||||
|
CMutex& operator=(const CMutex&);
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
void lock() const throw();
|
||||||
|
void unlock() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init();
|
||||||
|
void fini();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class CCondVarBase;
|
||||||
|
void* m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,131 @@
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "CThreadRep.h"
|
||||||
|
#include "XThread.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CThreadPtr
|
||||||
|
//
|
||||||
|
|
||||||
|
class CThreadPtr {
|
||||||
|
public:
|
||||||
|
CThreadPtr(CThreadRep* rep) : m_rep(rep) { }
|
||||||
|
~CThreadPtr() { m_rep->unref(); }
|
||||||
|
|
||||||
|
CThreadRep* operator->() const { return m_rep; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// not implemented
|
||||||
|
CThreadPtr(const CThreadPtr&);
|
||||||
|
CThreadPtr& operator=(const CThreadPtr&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CThreadRep* m_rep;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// CThread
|
||||||
|
//
|
||||||
|
|
||||||
|
CThread::CThread(IJob* job, void* userData)
|
||||||
|
{
|
||||||
|
m_rep = new CThreadRep(job, userData);
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread::CThread(const CThread& thread) : m_rep(thread.m_rep)
|
||||||
|
{
|
||||||
|
m_rep->ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread::CThread(CThreadRep* rep) : m_rep(rep)
|
||||||
|
{
|
||||||
|
// do nothing. rep should have already been Ref()'d.
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread::~CThread()
|
||||||
|
{
|
||||||
|
m_rep->unref();
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread& CThread::operator=(const CThread& thread)
|
||||||
|
{
|
||||||
|
if (thread.m_rep != m_rep) {
|
||||||
|
m_rep->unref();
|
||||||
|
m_rep = thread.m_rep;
|
||||||
|
m_rep->ref();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::sleep(double timeout)
|
||||||
|
{
|
||||||
|
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
if (timeout >= 0.0) {
|
||||||
|
currentRep->testCancel();
|
||||||
|
currentRep->sleep(timeout);
|
||||||
|
}
|
||||||
|
currentRep->testCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::exit(void* result)
|
||||||
|
{
|
||||||
|
throw XThreadExit(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThread::enableCancel(bool enable)
|
||||||
|
{
|
||||||
|
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
return currentRep->enableCancel(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::cancel()
|
||||||
|
{
|
||||||
|
m_rep->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::setPriority(int n)
|
||||||
|
{
|
||||||
|
m_rep->setPriority(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
CThread CThread::getCurrentThread()
|
||||||
|
{
|
||||||
|
return CThread(CThreadRep::getCurrentThreadRep());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThread::wait(double timeout) const
|
||||||
|
{
|
||||||
|
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
return currentRep->wait(m_rep, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThread::testCancel()
|
||||||
|
{
|
||||||
|
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
currentRep->testCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CThread::getResult() const
|
||||||
|
{
|
||||||
|
if (wait())
|
||||||
|
return m_rep->getResult();
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CThread::getUserData()
|
||||||
|
{
|
||||||
|
CThreadPtr currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
return currentRep->getUserData();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThread::operator==(const CThread& thread) const
|
||||||
|
{
|
||||||
|
return (m_rep == thread.m_rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThread::operator!=(const CThread& thread) const
|
||||||
|
{
|
||||||
|
return (m_rep != thread.m_rep);
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
#ifndef CTHREAD_H
|
||||||
|
#define CTHREAD_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class IJob;
|
||||||
|
class CThreadRep;
|
||||||
|
|
||||||
|
// note -- do not derive from this class
|
||||||
|
class CThread {
|
||||||
|
public:
|
||||||
|
// create and start a new thread executing the job.
|
||||||
|
// the user data can be retrieved with getUserData().
|
||||||
|
CThread(IJob* adopted, void* userData = 0);
|
||||||
|
|
||||||
|
// make a new thread object that refers to an existing thread.
|
||||||
|
// this does *not* start a new thread.
|
||||||
|
CThread(const CThread&);
|
||||||
|
|
||||||
|
// release thread. this does not terminate the thread. a thread
|
||||||
|
// will keep running until the job completes or calls exit().
|
||||||
|
~CThread();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// assign thread. this has no effect on the threads. it simply
|
||||||
|
// makes this thread object refer to another thread. it does *not*
|
||||||
|
// start a new thread.
|
||||||
|
CThread& operator=(const CThread&);
|
||||||
|
|
||||||
|
// the calling thread sleeps for the given number of seconds. if
|
||||||
|
// timeout <= 0.0 then the call returns immediately. if timeout
|
||||||
|
// == 0.0 then the calling thread yields the CPU.
|
||||||
|
// (cancellation point)
|
||||||
|
static void sleep(double timeout);
|
||||||
|
|
||||||
|
// terminate the calling thread. this function does not return but
|
||||||
|
// the stack is unwound and automatic objects are destroyed, as if
|
||||||
|
// exit() threw an exception (which is, in fact, what it does). the
|
||||||
|
// argument is saved as the result returned by getResult(). if you
|
||||||
|
// have a catch(...) block then you should add the following before
|
||||||
|
// it to avoid catching the exit: catch(CThreadExit&) { throw; }
|
||||||
|
static void exit(void*);
|
||||||
|
|
||||||
|
// enable/disable cancellation. default is enabled. this is not
|
||||||
|
// a cancellation point so if you enabled cancellation and want to
|
||||||
|
// allow immediate cancellation you need to call testCancel().
|
||||||
|
// return value is the previous state.
|
||||||
|
static bool enableCancel(bool);
|
||||||
|
|
||||||
|
// cancel the thread. cancel() never waits for the thread to
|
||||||
|
// terminate; it just posts the cancel and returns. a thread will
|
||||||
|
// terminate when it enters a cancellation point with cancellation
|
||||||
|
// enabled. if cancellation is disabled then the cancel is
|
||||||
|
// remembered but not acted on until the first call to a
|
||||||
|
// cancellation point after cancellation is enabled.
|
||||||
|
//
|
||||||
|
// a cancellation point is a function that can act on cancellation.
|
||||||
|
// a cancellation point does not return if there's a cancel pending.
|
||||||
|
// instead, it unwinds the stack and destroys automatic objects, as
|
||||||
|
// if cancel() threw an exception (which is, in fact, what it does).
|
||||||
|
// threads must take care to clean up and release any resources they
|
||||||
|
// may have, especially mutexes. they can catch (XThreadCancel) to
|
||||||
|
// do that then rethrow the exception or they can let it happen
|
||||||
|
// automatically by doing clean up in the d'tors of automatic
|
||||||
|
// objects. clients are strongly encouraged to do the latter.
|
||||||
|
// during cancellation, further cancel() calls are ignored (i.e.
|
||||||
|
// a thread cannot be interrupted by a cancel during cancellation).
|
||||||
|
//
|
||||||
|
// clients that catch (XThreadCancel) must always rethrow the
|
||||||
|
// exception. clients that catch(...) must either rethrow the
|
||||||
|
// exception or include a catch (XThreadCancel) handler that
|
||||||
|
// rethrows.
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
// change the priority of the thread. normal priority is 0, 1 is
|
||||||
|
// the next lower, etc. -1 is the next higher, etc. but boosting
|
||||||
|
// the priority may not be available.
|
||||||
|
void setPriority(int n);
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// return a thread object representing the calling thread
|
||||||
|
static CThread getCurrentThread();
|
||||||
|
|
||||||
|
// get the user data passed to the constructor for the current
|
||||||
|
// thread.
|
||||||
|
static void* getUserData();
|
||||||
|
|
||||||
|
// testCancel() does nothing but is a cancellation point. call
|
||||||
|
// this to make a function itself a cancellation point.
|
||||||
|
// (cancellation point)
|
||||||
|
static void testCancel();
|
||||||
|
|
||||||
|
// waits for the thread to terminate (by exit() or cancel() or
|
||||||
|
// by returning from the thread job). returns immediately if
|
||||||
|
// the thread has already terminated. returns immediately with
|
||||||
|
// false if called by a thread on itself. returns false on
|
||||||
|
// timeout (or error) and true on success.
|
||||||
|
// (cancellation point)
|
||||||
|
bool wait(double timeout = -1.0) const;
|
||||||
|
|
||||||
|
// get the exit result. does an implicit wait(). returns NULL
|
||||||
|
// immediately if called by a thread on itself. returns NULL for
|
||||||
|
// threads that were cancelled.
|
||||||
|
// (cancellation point)
|
||||||
|
void* getResult() const;
|
||||||
|
|
||||||
|
// compare threads for (in)equality
|
||||||
|
bool operator==(const CThread&) const;
|
||||||
|
bool operator!=(const CThread&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CThread(CThreadRep*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CThreadRep* m_rep;
|
||||||
|
};
|
||||||
|
|
||||||
|
// disables cancellation in the c'tor and enables it in the d'tor.
|
||||||
|
class CThreadMaskCancel {
|
||||||
|
public:
|
||||||
|
CThreadMaskCancel() : m_old(CThread::enableCancel(false)) { }
|
||||||
|
~CThreadMaskCancel() { CThread::enableCancel(m_old); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_old;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,518 @@
|
||||||
|
#include "CThreadRep.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "XThread.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "IJob.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// FIXME -- temporary exception type
|
||||||
|
class XThreadUnavailable { };
|
||||||
|
|
||||||
|
//
|
||||||
|
// CThreadRep
|
||||||
|
//
|
||||||
|
|
||||||
|
CMutex CThreadRep::s_mutex;
|
||||||
|
CThreadRep* CThreadRep::s_head = NULL;
|
||||||
|
|
||||||
|
CThreadRep::CThreadRep() : m_prev(NULL),
|
||||||
|
m_next(NULL),
|
||||||
|
m_refCount(1),
|
||||||
|
m_job(NULL),
|
||||||
|
m_userData(NULL)
|
||||||
|
{
|
||||||
|
// note -- s_mutex must be locked on entry
|
||||||
|
|
||||||
|
// initialize stuff
|
||||||
|
init();
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
// get main thread id
|
||||||
|
m_thread = pthread_self();
|
||||||
|
|
||||||
|
// install SIGALRM handler
|
||||||
|
struct sigaction act;
|
||||||
|
act.sa_handler = &threadCancel;
|
||||||
|
# if defined(SA_INTERRUPT)
|
||||||
|
act.sa_flags = SA_INTERRUPT;
|
||||||
|
# else
|
||||||
|
act.sa_flags = 0;
|
||||||
|
# endif
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
sigaction(SIGALRM, &act, NULL);
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
// get main thread id
|
||||||
|
m_thread = NULL;
|
||||||
|
m_id = GetCurrentThreadId();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// insert ourself into linked list
|
||||||
|
if (s_head != NULL) {
|
||||||
|
s_head->m_prev = this;
|
||||||
|
m_next = s_head;
|
||||||
|
}
|
||||||
|
s_head = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CThreadRep::CThreadRep(IJob* job, void* userData) :
|
||||||
|
m_prev(NULL),
|
||||||
|
m_next(NULL),
|
||||||
|
m_refCount(2), // 1 for us, 1 for thread
|
||||||
|
m_job(job),
|
||||||
|
m_userData(userData)
|
||||||
|
{
|
||||||
|
assert(m_job != NULL);
|
||||||
|
|
||||||
|
// create a thread rep for the main thread if the current thread
|
||||||
|
// is unknown. note that this might cause multiple "main" threads
|
||||||
|
// if threads are created external to this library.
|
||||||
|
getCurrentThreadRep()->unref();
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
init();
|
||||||
|
|
||||||
|
// hold mutex while we create the thread
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
|
||||||
|
// start the thread. throw if it doesn't start.
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
int status = pthread_create(&m_thread, NULL, threadFunc, (void*)this);
|
||||||
|
if (status != 0)
|
||||||
|
throw XThreadUnavailable();
|
||||||
|
sigset_t sigset;
|
||||||
|
sigemptyset(&sigset);
|
||||||
|
sigaddset(&sigset, SIGALRM);
|
||||||
|
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
unsigned int id;
|
||||||
|
m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0,
|
||||||
|
threadFunc, (void*)this, 0, &id));
|
||||||
|
m_id = static_cast<DWORD>(id);
|
||||||
|
if (m_thread == 0)
|
||||||
|
throw XThreadUnavailable();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// insert ourself into linked list
|
||||||
|
if (s_head != NULL) {
|
||||||
|
s_head->m_prev = this;
|
||||||
|
m_next = s_head;
|
||||||
|
}
|
||||||
|
s_head = this;
|
||||||
|
|
||||||
|
// returning releases the locks, allowing the child thread to run
|
||||||
|
}
|
||||||
|
|
||||||
|
CThreadRep::~CThreadRep()
|
||||||
|
{
|
||||||
|
// note -- s_mutex must be locked on entry
|
||||||
|
|
||||||
|
// remove ourself from linked list
|
||||||
|
if (m_prev != NULL) {
|
||||||
|
m_prev->m_next = m_next;
|
||||||
|
}
|
||||||
|
if (m_next != NULL) {
|
||||||
|
m_next->m_prev = m_prev;
|
||||||
|
}
|
||||||
|
if (s_head == this) {
|
||||||
|
s_head = m_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
fini();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::ref()
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
++m_refCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::unref()
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
if (--m_refCount == 0) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThreadRep::enableCancel(bool enable)
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
const bool old = m_cancellable;
|
||||||
|
m_cancellable = enable;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThreadRep::isCancellable() const
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
return (m_cancellable && !m_cancelling);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CThreadRep::getResult() const
|
||||||
|
{
|
||||||
|
// no lock necessary since thread isn't running
|
||||||
|
return m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CThreadRep::getUserData() const
|
||||||
|
{
|
||||||
|
// no lock necessary because the value never changes
|
||||||
|
return m_userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
CThreadRep* CThreadRep::getCurrentThreadRep()
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
const pthread_t thread = pthread_self();
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
const DWORD id = GetCurrentThreadId();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// lock list while we search
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
|
||||||
|
// search
|
||||||
|
CThreadRep* scan = s_head;
|
||||||
|
while (scan != NULL) {
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
if (scan->m_thread == thread) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
if (scan->m_id == id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
scan = scan->m_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create and use main thread rep if thread not found
|
||||||
|
if (scan == NULL) {
|
||||||
|
scan = new CThreadRep();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ref for caller
|
||||||
|
++scan->m_refCount;
|
||||||
|
|
||||||
|
return scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::doThreadFunc()
|
||||||
|
{
|
||||||
|
// default priority is slightly below normal
|
||||||
|
setPriority(1);
|
||||||
|
|
||||||
|
// wait for parent to initialize this object
|
||||||
|
{ CLock lock(&s_mutex); }
|
||||||
|
|
||||||
|
void* result = NULL;
|
||||||
|
try {
|
||||||
|
// go
|
||||||
|
m_job->run();
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (XThreadCancel&) {
|
||||||
|
// client called cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (XThreadExit& e) {
|
||||||
|
// client called exit()
|
||||||
|
result = e.m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// note -- don't catch (...) to avoid masking bugs
|
||||||
|
|
||||||
|
// done with job
|
||||||
|
delete m_job;
|
||||||
|
|
||||||
|
// store exit result (no lock necessary because the result will
|
||||||
|
// not be accessed until m_exit is set)
|
||||||
|
m_result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
void CThreadRep::init()
|
||||||
|
{
|
||||||
|
m_result = NULL;
|
||||||
|
m_cancellable = true;
|
||||||
|
m_cancelling = false;
|
||||||
|
m_cancel = false;
|
||||||
|
m_exit = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::fini()
|
||||||
|
{
|
||||||
|
// main thread has NULL job
|
||||||
|
if (m_job != NULL) {
|
||||||
|
pthread_detach(m_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::sleep(double timeout)
|
||||||
|
{
|
||||||
|
if (timeout < 0.0)
|
||||||
|
return;
|
||||||
|
struct timespec t;
|
||||||
|
t.tv_sec = (long)timeout;
|
||||||
|
t.tv_nsec = (long)(1000000000.0 * (timeout - (double)t.tv_sec));
|
||||||
|
nanosleep(&t, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::cancel()
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
if (m_cancellable && !m_cancelling) {
|
||||||
|
m_cancel = true;
|
||||||
|
|
||||||
|
// break out of system calls
|
||||||
|
pthread_kill(m_thread, SIGALRM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::testCancel()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
// prevent further cancellation
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
if (!m_cancel || !m_cancellable || m_cancelling)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update state for cancel
|
||||||
|
m_cancel = false;
|
||||||
|
m_cancelling = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start cancel
|
||||||
|
throw XThreadCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThreadRep::wait(CThreadRep* target, double timeout)
|
||||||
|
{
|
||||||
|
if (target == this)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
testCancel();
|
||||||
|
if (target->isExited())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (timeout > 0.0) {
|
||||||
|
CStopwatch timer;
|
||||||
|
do {
|
||||||
|
sleep(0.05);
|
||||||
|
testCancel();
|
||||||
|
if (target->isExited())
|
||||||
|
return true;
|
||||||
|
} while (timer.getTime() <= timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::setPriority(int)
|
||||||
|
{
|
||||||
|
// FIXME
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThreadRep::isExited() const
|
||||||
|
{
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
return m_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CThreadRep::threadFunc(void* arg)
|
||||||
|
{
|
||||||
|
CThreadRep* rep = (CThreadRep*)arg;
|
||||||
|
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
|
||||||
|
|
||||||
|
// run thread
|
||||||
|
rep->doThreadFunc();
|
||||||
|
|
||||||
|
// unref thread
|
||||||
|
rep->unref();
|
||||||
|
|
||||||
|
// mark as terminated
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
rep->m_exit = true;
|
||||||
|
|
||||||
|
// terminate the thread
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::threadCancel(int)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
|
||||||
|
#include <process.h>
|
||||||
|
|
||||||
|
void CThreadRep::init()
|
||||||
|
{
|
||||||
|
m_result = NULL;
|
||||||
|
m_cancellable = true;
|
||||||
|
m_cancelling = false;
|
||||||
|
m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::fini()
|
||||||
|
{
|
||||||
|
// destroy the events
|
||||||
|
CloseHandle(m_cancel);
|
||||||
|
CloseHandle(m_exit);
|
||||||
|
|
||||||
|
// close the handle (main thread has a NULL handle)
|
||||||
|
if (m_thread != NULL) {
|
||||||
|
CloseHandle(m_thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::sleep(double timeout)
|
||||||
|
{
|
||||||
|
if (isCancellable())
|
||||||
|
WaitForSingleObject(m_cancel, (DWORD)(1000.0 * timeout));
|
||||||
|
else
|
||||||
|
::Sleep((DWORD)(1000.0 * timeout));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::cancel()
|
||||||
|
{
|
||||||
|
SetEvent(m_cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::testCancel()
|
||||||
|
{
|
||||||
|
// poll cancel event. return if not set.
|
||||||
|
const DWORD result = ::WaitForSingleObject(getCancelEvent(), 0);
|
||||||
|
if (result != WAIT_OBJECT_0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
// ignore if disabled or already cancelling
|
||||||
|
CLock lock(&s_mutex);
|
||||||
|
if (!m_cancellable || m_cancelling)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// update state for cancel
|
||||||
|
m_cancelling = true;
|
||||||
|
ResetEvent(m_cancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// start cancel
|
||||||
|
throw XThreadCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CThreadRep::wait(CThreadRep* target, double timeout)
|
||||||
|
{
|
||||||
|
// get the current thread. if it's the same as the target thread
|
||||||
|
// then the thread is waiting on itself.
|
||||||
|
CRefCountedPtr<CThreadRep> currentRep(CThreadRep::getCurrentThreadRep());
|
||||||
|
if (target == this)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// is cancellation enabled?
|
||||||
|
const DWORD n = (isCancellable() ? 2 : 1);
|
||||||
|
|
||||||
|
// convert timeout
|
||||||
|
DWORD t;
|
||||||
|
if (timeout < 0.0)
|
||||||
|
t = INFINITE;
|
||||||
|
else
|
||||||
|
t = (DWORD)(1000.0 * timeout);
|
||||||
|
|
||||||
|
// wait for this thread to be cancelled or for the target thread to
|
||||||
|
// terminate.
|
||||||
|
HANDLE handles[2];
|
||||||
|
handles[0] = target->getExitEvent();
|
||||||
|
handles[1] = m_cancel;
|
||||||
|
DWORD result = ::WaitForMultipleObjects(n, handles, FALSE, t);
|
||||||
|
|
||||||
|
// cancel takes priority
|
||||||
|
if (n == 2 && result != WAIT_OBJECT_0 + 1 &&
|
||||||
|
::WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0)
|
||||||
|
result = WAIT_OBJECT_0 + 1;
|
||||||
|
|
||||||
|
// handle result
|
||||||
|
switch (result) {
|
||||||
|
case WAIT_OBJECT_0 + 0:
|
||||||
|
// target thread terminated
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case WAIT_OBJECT_0 + 1:
|
||||||
|
// this thread was cancelled. does not return.
|
||||||
|
testCancel();
|
||||||
|
|
||||||
|
default:
|
||||||
|
// error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CThreadRep::setPriority(int n)
|
||||||
|
{
|
||||||
|
if (n < 0) {
|
||||||
|
switch (-n) {
|
||||||
|
case 1: n = THREAD_PRIORITY_ABOVE_NORMAL; break;
|
||||||
|
default: n = THREAD_PRIORITY_HIGHEST; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (n) {
|
||||||
|
case 0: n = THREAD_PRIORITY_NORMAL; break;
|
||||||
|
case 1: n = THREAD_PRIORITY_BELOW_NORMAL; break;
|
||||||
|
case 2: n = THREAD_PRIORITY_LOWEST; break;
|
||||||
|
default: n = THREAD_PRIORITY_IDLE; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetThreadPriority(m_thread, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE CThreadRep::getExitEvent() const
|
||||||
|
{
|
||||||
|
// no lock necessary because the value never changes
|
||||||
|
return m_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE CThreadRep::getCancelEvent() const
|
||||||
|
{
|
||||||
|
// no lock necessary because the value never changes
|
||||||
|
return m_cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int __stdcall CThreadRep::threadFunc(void* arg)
|
||||||
|
{
|
||||||
|
CThreadRep* rep = (CThreadRep*)arg;
|
||||||
|
|
||||||
|
// initialize OLE
|
||||||
|
const HRESULT hr = ::OleInitialize(NULL);
|
||||||
|
|
||||||
|
// run thread
|
||||||
|
rep->doThreadFunc();
|
||||||
|
|
||||||
|
// close OLE
|
||||||
|
if (!FAILED(hr)) {
|
||||||
|
OleUninitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// signal termination
|
||||||
|
SetEvent(rep->m_exit);
|
||||||
|
|
||||||
|
// unref thread
|
||||||
|
rep->unref();
|
||||||
|
|
||||||
|
// terminate the thread
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,122 @@
|
||||||
|
#ifndef CTHREADREP_H
|
||||||
|
#define CTHREADREP_H
|
||||||
|
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
#include <pthread.h>
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class IJob;
|
||||||
|
|
||||||
|
class CThreadRep {
|
||||||
|
public:
|
||||||
|
CThreadRep(IJob*, void* userData);
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// change ref count
|
||||||
|
void ref();
|
||||||
|
void unref();
|
||||||
|
|
||||||
|
// the calling thread sleeps for t seconds. if t == 0.0 then
|
||||||
|
// the thread yields the CPU.
|
||||||
|
void sleep(double timeout);
|
||||||
|
|
||||||
|
// cancel the thread
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
// set cancellation state
|
||||||
|
bool enableCancel(bool enable);
|
||||||
|
|
||||||
|
// permanently disable further cancellation and start cancel cleanup
|
||||||
|
// if cancel has been called and cancellation hasn't been started yet.
|
||||||
|
void testCancel();
|
||||||
|
|
||||||
|
// wait for thread to exit or for current thread to cancel
|
||||||
|
bool wait(CThreadRep*, double timeout);
|
||||||
|
|
||||||
|
// set the priority
|
||||||
|
void setPriority(int n);
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// get the exit result for this thread. thread must be terminated.
|
||||||
|
void* getResult() const;
|
||||||
|
|
||||||
|
// get the user data passed to the constructor
|
||||||
|
void* getUserData() const;
|
||||||
|
|
||||||
|
// get the current cancellable state
|
||||||
|
bool isCancellable() const;
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
bool isExited() const;
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
HANDLE getExitEvent() const;
|
||||||
|
HANDLE getCancelEvent() const;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// return the thread rep for the calling thread. the returned
|
||||||
|
// rep has been ref()'d.
|
||||||
|
static CThreadRep* getCurrentThreadRep();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~CThreadRep();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// internal constructor
|
||||||
|
CThreadRep();
|
||||||
|
|
||||||
|
// initialization/cleanup
|
||||||
|
void init();
|
||||||
|
void fini();
|
||||||
|
|
||||||
|
// thread rep lookup
|
||||||
|
static CThreadRep* find();
|
||||||
|
|
||||||
|
// thread functions
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
static void* threadFunc(void* arg);
|
||||||
|
static void threadCancel(int);
|
||||||
|
#elif defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
static unsigned int __stdcall threadFunc(void* arg);
|
||||||
|
#endif
|
||||||
|
void doThreadFunc();
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
CThreadRep(const CThreadRep&);
|
||||||
|
CThreadRep& operator=(const CThreadRep&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static CMutex s_mutex;
|
||||||
|
static CThreadRep* s_head;
|
||||||
|
|
||||||
|
CThreadRep* m_prev;
|
||||||
|
CThreadRep* m_next;
|
||||||
|
|
||||||
|
SInt32 m_refCount;
|
||||||
|
IJob* m_job;
|
||||||
|
void* m_userData;
|
||||||
|
void* m_result;
|
||||||
|
bool m_cancellable;
|
||||||
|
bool m_cancelling;
|
||||||
|
|
||||||
|
#if defined(CONFIG_PTHREADS)
|
||||||
|
pthread_t m_thread;
|
||||||
|
bool m_exit;
|
||||||
|
bool m_cancel;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_PLATFORM_WIN32)
|
||||||
|
HANDLE m_thread;
|
||||||
|
DWORD m_id;
|
||||||
|
HANDLE m_exit;
|
||||||
|
HANDLE m_cancel;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "CTimerThread.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "TMethodJob.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CTimerThread
|
||||||
|
//
|
||||||
|
|
||||||
|
CTimerThread::CTimerThread(double timeout) : m_timeout(timeout)
|
||||||
|
{
|
||||||
|
assert(m_timeout > 0.0);
|
||||||
|
m_callingThread = new CThread(CThread::getCurrentThread());
|
||||||
|
m_timingThread = new CThread(new TMethodJob<CTimerThread>(
|
||||||
|
this, &CTimerThread::timer));
|
||||||
|
}
|
||||||
|
|
||||||
|
CTimerThread::~CTimerThread()
|
||||||
|
{
|
||||||
|
m_timingThread->cancel();
|
||||||
|
delete m_timingThread;
|
||||||
|
delete m_callingThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTimerThread::timer(void*)
|
||||||
|
{
|
||||||
|
CThread::sleep(m_timeout);
|
||||||
|
m_callingThread->cancel();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef CTIMERTHREAD_H
|
||||||
|
#define CTIMERTHREAD_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
class CThread;
|
||||||
|
|
||||||
|
class CTimerThread {
|
||||||
|
public:
|
||||||
|
CTimerThread(double timeout);
|
||||||
|
~CTimerThread();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void timer(void*);
|
||||||
|
|
||||||
|
// not implemented
|
||||||
|
CTimerThread(const CTimerThread&);
|
||||||
|
CTimerThread& operator=(const CTimerThread&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_timeout;
|
||||||
|
CThread* m_callingThread;
|
||||||
|
CThread* m_timingThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
DEPTH=..
|
||||||
|
include $(DEPTH)/Makecommon
|
||||||
|
|
||||||
|
#
|
||||||
|
# target file
|
||||||
|
#
|
||||||
|
TARGET = mt
|
||||||
|
|
||||||
|
#
|
||||||
|
# source files
|
||||||
|
#
|
||||||
|
LCXXINCS = \
|
||||||
|
-I$(DEPTH)/base \
|
||||||
|
$(NULL)
|
||||||
|
CXXFILES = \
|
||||||
|
CLock.cpp \
|
||||||
|
CMutex.cpp \
|
||||||
|
CCondVar.cpp \
|
||||||
|
CThread.cpp \
|
||||||
|
CThreadRep.cpp \
|
||||||
|
CTimerThread.cpp \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
targets: $(LIBTARGET)
|
||||||
|
|
||||||
|
$(LIBTARGET): $(OBJECTS)
|
||||||
|
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
|
||||||
|
$(ARF) $(LIBTARGET) $(OBJECTS)
|
|
@ -0,0 +1,31 @@
|
||||||
|
#ifndef XTHREAD_H
|
||||||
|
#define XTHREAD_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
// generic thread exception
|
||||||
|
class XThread { };
|
||||||
|
|
||||||
|
// thrown by CThread::Exit() to exit a thread. clients of CThread
|
||||||
|
// must not throw this type but must rethrow it if caught (by
|
||||||
|
// XThreadExit, XThread, or ...).
|
||||||
|
class XThreadExit : public XThread {
|
||||||
|
public:
|
||||||
|
XThreadExit(void* result) : m_result(result) { }
|
||||||
|
~XThreadExit() { }
|
||||||
|
|
||||||
|
public:
|
||||||
|
void* m_result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// thrown to cancel a thread. clients must not throw this type, but
|
||||||
|
// must rethrow it if caught (by XThreadCancel, XThread, or ...).
|
||||||
|
class XThreadCancel : public XThread { };
|
||||||
|
|
||||||
|
// convenience macro to rethrow an XThread exception but ignore other
|
||||||
|
// exceptions. put this in your catch (...) handler after necessary
|
||||||
|
// cleanup but before leaving or returning from the handler.
|
||||||
|
#define RETHROW_XTHREAD \
|
||||||
|
try { throw; } catch (XThread&) { throw; } catch (...) { }
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include "CNetworkAddress.h"
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CNetworkAddress
|
||||||
|
//
|
||||||
|
|
||||||
|
CNetworkAddress::CNetworkAddress(UInt16 port) throw(XSocketAddress)
|
||||||
|
{
|
||||||
|
if (port == 0)
|
||||||
|
throw XSocketAddress(XSocketAddress::kBadPort, CString(), port);
|
||||||
|
|
||||||
|
struct sockaddr_in* inetAddress = reinterpret_cast<struct sockaddr_in*>(&m_address);
|
||||||
|
inetAddress->sin_family = AF_INET;
|
||||||
|
inetAddress->sin_port = htons(port);
|
||||||
|
inetAddress->sin_addr.s_addr = INADDR_ANY;
|
||||||
|
::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
CNetworkAddress::CNetworkAddress(const CString& hostname, UInt16 port)
|
||||||
|
throw(XSocketAddress)
|
||||||
|
{
|
||||||
|
if (port == 0)
|
||||||
|
throw XSocketAddress(XSocketAddress::kBadPort, hostname, port);
|
||||||
|
|
||||||
|
struct hostent* hent = gethostbyname(hostname.c_str());
|
||||||
|
if (hent == NULL) {
|
||||||
|
switch (h_errno) {
|
||||||
|
case HOST_NOT_FOUND:
|
||||||
|
throw XSocketAddress(XSocketAddress::kNotFound, hostname, port);
|
||||||
|
|
||||||
|
case NO_DATA:
|
||||||
|
throw XSocketAddress(XSocketAddress::kNoAddress, hostname, port);
|
||||||
|
|
||||||
|
case NO_RECOVERY:
|
||||||
|
case TRY_AGAIN:
|
||||||
|
default:
|
||||||
|
throw XSocketAddress(XSocketAddress::kUnknown, hostname, port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in* inetAddress = reinterpret_cast<struct sockaddr_in*>(&m_address);
|
||||||
|
inetAddress->sin_family = hent->h_addrtype;
|
||||||
|
inetAddress->sin_port = htons(port);
|
||||||
|
::memcpy(&inetAddress->sin_addr, hent->h_addr_list[0], hent->h_length);
|
||||||
|
::memset(inetAddress->sin_zero, 0, sizeof(inetAddress->sin_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
CNetworkAddress::~CNetworkAddress()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sockaddr* CNetworkAddress::getAddress() const throw()
|
||||||
|
{
|
||||||
|
return &m_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CNetworkAddress::getAddressLength() const throw()
|
||||||
|
{
|
||||||
|
return sizeof(m_address);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef CNETWORKADDRESS_H
|
||||||
|
#define CNETWORKADDRESS_H
|
||||||
|
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include "XSocket.h"
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
class CString;
|
||||||
|
|
||||||
|
class CNetworkAddress {
|
||||||
|
public:
|
||||||
|
CNetworkAddress(UInt16 port) throw(XSocketAddress);
|
||||||
|
CNetworkAddress(const CString& hostname, UInt16 port) throw(XSocketAddress);
|
||||||
|
~CNetworkAddress();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
const struct sockaddr* getAddress() const throw();
|
||||||
|
int getAddressLength() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct sockaddr m_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include "CSocketInputStream.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "IJob.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CSocketInputStream
|
||||||
|
//
|
||||||
|
|
||||||
|
CSocketInputStream::CSocketInputStream(CMutex* mutex, IJob* closeCB) :
|
||||||
|
m_mutex(mutex),
|
||||||
|
m_empty(mutex, true),
|
||||||
|
m_closeCB(closeCB),
|
||||||
|
m_closed(false),
|
||||||
|
m_hungup(false)
|
||||||
|
{
|
||||||
|
assert(m_mutex != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSocketInputStream::~CSocketInputStream()
|
||||||
|
{
|
||||||
|
delete m_closeCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketInputStream::write(
|
||||||
|
const void* data, UInt32 n) throw()
|
||||||
|
{
|
||||||
|
if (!m_hungup && n > 0) {
|
||||||
|
m_buffer.write(data, n);
|
||||||
|
m_empty = (m_buffer.getSize() == 0);
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketInputStream::hangup() throw()
|
||||||
|
{
|
||||||
|
m_hungup = true;
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketInputStream::close() throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_closed = true;
|
||||||
|
if (m_closeCB) {
|
||||||
|
m_closeCB->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketInputStream::read(
|
||||||
|
void* dst, UInt32 n) throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for data (or hangup)
|
||||||
|
while (!m_hungup && m_empty == true) {
|
||||||
|
m_empty.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// read data
|
||||||
|
const UInt32 count = m_buffer.getSize();
|
||||||
|
if (n > count) {
|
||||||
|
n = count;
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
|
::memcpy(dst, m_buffer.peek(n), n);
|
||||||
|
m_buffer.pop(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// update empty state
|
||||||
|
if (m_buffer.getSize() == 0) {
|
||||||
|
m_empty = true;
|
||||||
|
m_empty.broadcast();
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketInputStream::getSize() const throw()
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef CSOCKETINPUTSTREAM_H
|
||||||
|
#define CSOCKETINPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "CSocketStreamBuffer.h"
|
||||||
|
#include "CCondVar.h"
|
||||||
|
#include "IInputStream.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class IJob;
|
||||||
|
|
||||||
|
class CSocketInputStream : public IInputStream {
|
||||||
|
public:
|
||||||
|
CSocketInputStream(CMutex*, IJob* adoptedCloseCB);
|
||||||
|
~CSocketInputStream();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// write() appends n bytes to the buffer
|
||||||
|
void write(const void*, UInt32 n) throw();
|
||||||
|
|
||||||
|
// causes read() to always return immediately. if there is no
|
||||||
|
// more data then it returns 0. further writes are discarded.
|
||||||
|
void hangup() throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// IInputStream overrides
|
||||||
|
// these all lock the mutex for their duration
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
virtual UInt32 read(void*, UInt32 count) throw(XIO);
|
||||||
|
virtual UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex* m_mutex;
|
||||||
|
CCondVar<bool> m_empty;
|
||||||
|
IJob* m_closeCB;
|
||||||
|
CSocketStreamBuffer m_buffer;
|
||||||
|
bool m_closed;
|
||||||
|
bool m_hungup;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include "CSocketOutputStream.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "IJob.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CSocketOutputStream
|
||||||
|
//
|
||||||
|
|
||||||
|
CSocketOutputStream::CSocketOutputStream(CMutex* mutex, IJob* closeCB) :
|
||||||
|
m_mutex(mutex),
|
||||||
|
m_closeCB(closeCB),
|
||||||
|
m_closed(false)
|
||||||
|
{
|
||||||
|
assert(m_mutex != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSocketOutputStream::~CSocketOutputStream()
|
||||||
|
{
|
||||||
|
delete m_closeCB;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* CSocketOutputStream::peek(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
return m_buffer.peek(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketOutputStream::pop(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
m_buffer.pop(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketOutputStream::getSize() const throw()
|
||||||
|
{
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketOutputStream::close() throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_closed = true;
|
||||||
|
if (m_closeCB) {
|
||||||
|
m_closeCB->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketOutputStream::write(
|
||||||
|
const void* data, UInt32 n) throw(XIO)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_closed) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer.write(data, n);
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketOutputStream::flush() throw(XIO)
|
||||||
|
{
|
||||||
|
// wait until all data is written
|
||||||
|
while (getSizeWithLock() > 0) {
|
||||||
|
CThread::sleep(0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketOutputStream::getSizeWithLock() const throw()
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
return m_buffer.getSize();
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef CSOCKETOUTPUTSTREAM_H
|
||||||
|
#define CSOCKETOUTPUTSTREAM_H
|
||||||
|
|
||||||
|
#include "CSocketStreamBuffer.h"
|
||||||
|
#include "IOutputStream.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class IJob;
|
||||||
|
|
||||||
|
class CSocketOutputStream : public IOutputStream {
|
||||||
|
public:
|
||||||
|
CSocketOutputStream(CMutex*, IJob* adoptedCloseCB);
|
||||||
|
~CSocketOutputStream();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||||
|
// pop() discards the next n bytes.
|
||||||
|
const void* peek(UInt32 n) throw();
|
||||||
|
void pop(UInt32 n) throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// return the number of bytes in the buffer
|
||||||
|
UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
// IOutputStream overrides
|
||||||
|
// these all lock the mutex for their duration
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
virtual UInt32 write(const void*, UInt32 count) throw(XIO);
|
||||||
|
virtual void flush() throw(XIO);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UInt32 getSizeWithLock() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CMutex* m_mutex;
|
||||||
|
IJob* m_closeCB;
|
||||||
|
CSocketStreamBuffer m_buffer;
|
||||||
|
bool m_closed;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "CSocketStreamBuffer.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CSocketStreamBuffer
|
||||||
|
//
|
||||||
|
|
||||||
|
const UInt32 CSocketStreamBuffer::kChunkSize = 4096;
|
||||||
|
|
||||||
|
CSocketStreamBuffer::CSocketStreamBuffer() : m_size(0)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
CSocketStreamBuffer::~CSocketStreamBuffer()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* CSocketStreamBuffer::peek(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
assert(n <= m_size);
|
||||||
|
|
||||||
|
// reserve space in first chunk
|
||||||
|
ChunkList::iterator head = m_chunks.begin();
|
||||||
|
head->reserve(n);
|
||||||
|
|
||||||
|
// consolidate chunks into the first chunk until it has n bytes
|
||||||
|
ChunkList::iterator scan = head;
|
||||||
|
++scan;
|
||||||
|
while (head->size() < n && scan != m_chunks.end()) {
|
||||||
|
head->insert(head->end(), scan->begin(), scan->end());
|
||||||
|
scan = m_chunks.erase(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<const void*>(head->begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketStreamBuffer::pop(UInt32 n) throw()
|
||||||
|
{
|
||||||
|
m_size -= n;
|
||||||
|
|
||||||
|
// discard chunks until more than n bytes would've been discarded
|
||||||
|
ChunkList::iterator scan = m_chunks.begin();
|
||||||
|
while (scan->size() <= n && scan != m_chunks.end()) {
|
||||||
|
n -= scan->size();
|
||||||
|
scan = m_chunks.erase(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there's anything left over then remove it from the head chunk.
|
||||||
|
// if there's no head chunk then we're already empty.
|
||||||
|
if (scan == m_chunks.end()) {
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
else if (n > 0) {
|
||||||
|
scan->erase(scan->begin(), scan->begin() + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSocketStreamBuffer::write(
|
||||||
|
const void* vdata, UInt32 n) throw()
|
||||||
|
{
|
||||||
|
assert(vdata != NULL);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_size += n;
|
||||||
|
|
||||||
|
// cast data to bytes
|
||||||
|
const UInt8* data = reinterpret_cast<const UInt8*>(vdata);
|
||||||
|
|
||||||
|
// point to last chunk if it has space, otherwise append an empty chunk
|
||||||
|
ChunkList::iterator scan = m_chunks.end();
|
||||||
|
if (scan != m_chunks.begin()) {
|
||||||
|
--scan;
|
||||||
|
if (scan->size() >= kChunkSize)
|
||||||
|
++scan;
|
||||||
|
}
|
||||||
|
if (scan == m_chunks.end()) {
|
||||||
|
scan = m_chunks.insert(scan);
|
||||||
|
}
|
||||||
|
|
||||||
|
// append data in chunks
|
||||||
|
while (n > 0) {
|
||||||
|
// choose number of bytes for next chunk
|
||||||
|
UInt32 count = kChunkSize - scan->size();
|
||||||
|
if (count > n)
|
||||||
|
count = n;
|
||||||
|
|
||||||
|
// transfer data
|
||||||
|
scan->insert(scan->end(), data, data + count);
|
||||||
|
n -= count;
|
||||||
|
data += count;
|
||||||
|
|
||||||
|
// append another empty chunk if we're not done yet
|
||||||
|
if (n > 0) {
|
||||||
|
scan = m_chunks.insert(scan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32 CSocketStreamBuffer::getSize() const throw()
|
||||||
|
{
|
||||||
|
return m_size;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef CSOCKETSTREAMBUFFER_H
|
||||||
|
#define CSOCKETSTREAMBUFFER_H
|
||||||
|
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class CSocketStreamBuffer {
|
||||||
|
public:
|
||||||
|
CSocketStreamBuffer();
|
||||||
|
~CSocketStreamBuffer();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// peek() returns a buffer of n bytes (which must be <= getSize()).
|
||||||
|
// pop() discards the next n bytes.
|
||||||
|
const void* peek(UInt32 n) throw();
|
||||||
|
void pop(UInt32 n) throw();
|
||||||
|
|
||||||
|
// write() appends n bytes to the buffer
|
||||||
|
void write(const void*, UInt32 n) throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// return the number of bytes in the buffer
|
||||||
|
UInt32 getSize() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const UInt32 kChunkSize;
|
||||||
|
|
||||||
|
typedef std::vector<UInt8> Chunk;
|
||||||
|
typedef std::list<Chunk> ChunkList;
|
||||||
|
|
||||||
|
ChunkList m_chunks;
|
||||||
|
UInt32 m_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "CTCPListenSocket.h"
|
||||||
|
#include "CTCPSocket.h"
|
||||||
|
#include "CNetworkAddress.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CTCPListenSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
CTCPListenSocket::CTCPListenSocket()
|
||||||
|
{
|
||||||
|
m_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (m_fd == -1) {
|
||||||
|
throw XSocketCreate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCPListenSocket::~CTCPListenSocket()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPListenSocket::bind(
|
||||||
|
const CNetworkAddress& addr) throw(XSocket)
|
||||||
|
{
|
||||||
|
if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||||
|
if (errno == EADDRINUSE) {
|
||||||
|
throw XSocketAddressInUse();
|
||||||
|
}
|
||||||
|
throw XSocketBind();
|
||||||
|
}
|
||||||
|
if (listen(m_fd, 3) == -1) {
|
||||||
|
throw XSocketBind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocket* CTCPListenSocket::accept() throw(XSocket)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
struct sockaddr addr;
|
||||||
|
socklen_t addrlen = sizeof(addr);
|
||||||
|
CThread::testCancel();
|
||||||
|
int fd = ::accept(m_fd, &addr, &addrlen);
|
||||||
|
if (fd == -1) {
|
||||||
|
CThread::testCancel();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new CTCPSocket(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPListenSocket::close() throw(XIO)
|
||||||
|
{
|
||||||
|
if (m_fd == -1) {
|
||||||
|
throw XIOClosed();
|
||||||
|
}
|
||||||
|
if (::close(m_fd) == -1) {
|
||||||
|
throw XIOClose();
|
||||||
|
}
|
||||||
|
m_fd = -1;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CTCPLISTENSOCKET_H
|
||||||
|
#define CTCPLISTENSOCKET_H
|
||||||
|
|
||||||
|
#include "IListenSocket.h"
|
||||||
|
|
||||||
|
class CTCPListenSocket : public IListenSocket {
|
||||||
|
public:
|
||||||
|
CTCPListenSocket();
|
||||||
|
~CTCPListenSocket();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// IListenSocket overrides
|
||||||
|
virtual void bind(const CNetworkAddress&) throw(XSocket);
|
||||||
|
virtual ISocket* accept() throw(XSocket);
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,226 @@
|
||||||
|
#include "CTCPSocket.h"
|
||||||
|
#include "CBufferedInputStream.h"
|
||||||
|
#include "CBufferedOutputStream.h"
|
||||||
|
#include "CNetworkAddress.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
|
#include "CCondVar.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#include "TMethodJob.h"
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
//
|
||||||
|
// CTCPSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
CTCPSocket::CTCPSocket() throw(XSocket)
|
||||||
|
{
|
||||||
|
m_fd = socket(PF_INET, SOCK_STREAM, 0);
|
||||||
|
if (m_fd == -1) {
|
||||||
|
throw XSocketCreate();
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCPSocket::CTCPSocket(int fd) throw() :
|
||||||
|
m_fd(fd)
|
||||||
|
{
|
||||||
|
assert(m_fd != -1);
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
// socket starts in connected state
|
||||||
|
m_connected = kReadWrite;
|
||||||
|
|
||||||
|
// start handling socket
|
||||||
|
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::service));
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCPSocket::~CTCPSocket()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
// ignore failures
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
delete m_mutex;
|
||||||
|
delete m_input;
|
||||||
|
delete m_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::bind(const CNetworkAddress& addr)
|
||||||
|
throw(XSocket)
|
||||||
|
{
|
||||||
|
if (::bind(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||||
|
if (errno == EADDRINUSE) {
|
||||||
|
throw XSocketAddressInUse();
|
||||||
|
}
|
||||||
|
throw XSocketBind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::connect(const CNetworkAddress& addr)
|
||||||
|
throw(XSocket)
|
||||||
|
{
|
||||||
|
CThread::testCancel();
|
||||||
|
if (::connect(m_fd, addr.getAddress(), addr.getAddressLength()) == -1) {
|
||||||
|
CThread::testCancel();
|
||||||
|
throw XSocketConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// start servicing the socket
|
||||||
|
m_connected = kReadWrite;
|
||||||
|
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::service));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::close() throw(XIO)
|
||||||
|
{
|
||||||
|
// shutdown I/O thread before close
|
||||||
|
if (m_thread != NULL) {
|
||||||
|
// flush if output buffer not empty and output buffer not closed
|
||||||
|
bool doFlush;
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
doFlush = ((m_connected & kWrite) != 0);
|
||||||
|
}
|
||||||
|
if (doFlush) {
|
||||||
|
m_output->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_thread->cancel();
|
||||||
|
m_thread->wait();
|
||||||
|
delete m_thread;
|
||||||
|
m_thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (m_fd != -1) {
|
||||||
|
if (::close(m_fd) == -1) {
|
||||||
|
throw XIOClose();
|
||||||
|
}
|
||||||
|
m_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IInputStream* CTCPSocket::getInputStream() throw()
|
||||||
|
{
|
||||||
|
return m_input;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOutputStream* CTCPSocket::getOutputStream() throw()
|
||||||
|
{
|
||||||
|
return m_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::init() throw(XIO)
|
||||||
|
{
|
||||||
|
m_mutex = new CMutex;
|
||||||
|
m_thread = NULL;
|
||||||
|
m_connected = kClosed;
|
||||||
|
m_input = new CBufferedInputStream(m_mutex,
|
||||||
|
new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::closeInput));
|
||||||
|
m_output = new CBufferedOutputStream(m_mutex,
|
||||||
|
new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::closeOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::service(void*) throw(XThread)
|
||||||
|
{
|
||||||
|
assert(m_fd != -1);
|
||||||
|
|
||||||
|
// now service the connection
|
||||||
|
struct pollfd pfds[1];
|
||||||
|
pfds[0].fd = m_fd;
|
||||||
|
for (;;) {
|
||||||
|
{
|
||||||
|
// choose events to poll for
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
pfds[0].events = 0;
|
||||||
|
if ((m_connected & kRead) != 0) {
|
||||||
|
// still open for reading
|
||||||
|
pfds[0].events |= POLLIN;
|
||||||
|
}
|
||||||
|
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
|
||||||
|
// data queued for writing
|
||||||
|
pfds[0].events |= POLLOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for status
|
||||||
|
CThread::testCancel();
|
||||||
|
const int status = poll(pfds, 1, 50);
|
||||||
|
CThread::testCancel();
|
||||||
|
|
||||||
|
// transfer data and handle errors
|
||||||
|
if (status == 1) {
|
||||||
|
if ((pfds[0].revents & (POLLERR | POLLNVAL)) != 0) {
|
||||||
|
// stream is no good anymore so bail
|
||||||
|
m_input->hangup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read some data
|
||||||
|
if (pfds[0].revents & POLLIN) {
|
||||||
|
UInt8 buffer[4096];
|
||||||
|
ssize_t n = read(m_fd, buffer, sizeof(buffer));
|
||||||
|
if (n > 0) {
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
m_input->write(buffer, n);
|
||||||
|
}
|
||||||
|
else if (n == 0) {
|
||||||
|
// stream hungup
|
||||||
|
m_input->hangup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write some data
|
||||||
|
if (pfds[0].revents & POLLOUT) {
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
|
||||||
|
// get amount of data to write
|
||||||
|
UInt32 n = m_output->getSize();
|
||||||
|
if (n > 4096) {
|
||||||
|
// limit write size
|
||||||
|
n = 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write data
|
||||||
|
const void* buffer = m_output->peek(n);
|
||||||
|
n = write(m_fd, buffer, n);
|
||||||
|
|
||||||
|
// discard written data
|
||||||
|
if (n > 0) {
|
||||||
|
m_output->pop(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::closeInput(void*) throw()
|
||||||
|
{
|
||||||
|
// note -- m_mutex should already be locked
|
||||||
|
shutdown(m_fd, 0);
|
||||||
|
m_connected &= ~kRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTCPSocket::closeOutput(void*) throw()
|
||||||
|
{
|
||||||
|
// note -- m_mutex should already be locked
|
||||||
|
shutdown(m_fd, 1);
|
||||||
|
m_connected &= ~kWrite;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef CTCPSOCKET_H
|
||||||
|
#define CTCPSOCKET_H
|
||||||
|
|
||||||
|
#include "ISocket.h"
|
||||||
|
#include "XThread.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
template <class T>
|
||||||
|
class CCondVar;
|
||||||
|
class CThread;
|
||||||
|
class CBufferedInputStream;
|
||||||
|
class CBufferedOutputStream;
|
||||||
|
|
||||||
|
class CTCPSocket : public ISocket {
|
||||||
|
public:
|
||||||
|
CTCPSocket() throw(XSocket);
|
||||||
|
CTCPSocket(int fd) throw();
|
||||||
|
~CTCPSocket();
|
||||||
|
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
// ISocket overrides
|
||||||
|
virtual void bind(const CNetworkAddress&) throw(XSocket);
|
||||||
|
virtual void connect(const CNetworkAddress&) throw(XSocket);
|
||||||
|
virtual void close() throw(XIO);
|
||||||
|
virtual IInputStream* getInputStream() throw();
|
||||||
|
virtual IOutputStream* getOutputStream() throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init() throw(XIO);
|
||||||
|
void service(void*) throw(XThread);
|
||||||
|
void closeInput(void*) throw();
|
||||||
|
void closeOutput(void*) throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 };
|
||||||
|
|
||||||
|
int m_fd;
|
||||||
|
CBufferedInputStream* m_input;
|
||||||
|
CBufferedOutputStream* m_output;
|
||||||
|
|
||||||
|
CMutex* m_mutex;
|
||||||
|
CThread* m_thread;
|
||||||
|
UInt32 m_connected;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef ILISTENSOCKET_H
|
||||||
|
#define ILISTENSOCKET_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
#include "XSocket.h"
|
||||||
|
|
||||||
|
class CNetworkAddress;
|
||||||
|
class ISocket;
|
||||||
|
|
||||||
|
class IListenSocket : public IInterface {
|
||||||
|
public:
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// bind the socket to a particular address
|
||||||
|
virtual void bind(const CNetworkAddress&) throw(XSocket) = 0;
|
||||||
|
|
||||||
|
// wait for a connection
|
||||||
|
virtual ISocket* accept() throw(XSocket) = 0;
|
||||||
|
|
||||||
|
// close the socket
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
#ifndef ISOCKET_H
|
||||||
|
#define ISOCKET_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
#include "XSocket.h"
|
||||||
|
#include "XIO.h"
|
||||||
|
|
||||||
|
class CNetworkAddress;
|
||||||
|
class IInputStream;
|
||||||
|
class IOutputStream;
|
||||||
|
|
||||||
|
class ISocket : public IInterface {
|
||||||
|
public:
|
||||||
|
// manipulators
|
||||||
|
|
||||||
|
// bind the socket to a particular address
|
||||||
|
virtual void bind(const CNetworkAddress&) throw(XSocket) = 0;
|
||||||
|
|
||||||
|
// connect the socket
|
||||||
|
virtual void connect(const CNetworkAddress&) throw(XSocket) = 0;
|
||||||
|
|
||||||
|
// close the socket. this will flush the output stream if it
|
||||||
|
// hasn't been closed yet.
|
||||||
|
virtual void close() throw(XIO) = 0;
|
||||||
|
|
||||||
|
// get the input and output streams for the socket. closing
|
||||||
|
// these streams closes the appropriate half of the socket.
|
||||||
|
virtual IInputStream* getInputStream() throw() = 0;
|
||||||
|
virtual IOutputStream* getOutputStream() throw() = 0;
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,28 @@
|
||||||
|
DEPTH=..
|
||||||
|
include $(DEPTH)/Makecommon
|
||||||
|
|
||||||
|
#
|
||||||
|
# target file
|
||||||
|
#
|
||||||
|
TARGET = net
|
||||||
|
|
||||||
|
#
|
||||||
|
# source files
|
||||||
|
#
|
||||||
|
LCXXINCS = \
|
||||||
|
-I$(DEPTH)/base \
|
||||||
|
-I$(DEPTH)/mt \
|
||||||
|
-I$(DEPTH)/io \
|
||||||
|
$(NULL)
|
||||||
|
CXXFILES = \
|
||||||
|
XSocket.cpp \
|
||||||
|
CNetworkAddress.cpp \
|
||||||
|
CTCPSocket.cpp \
|
||||||
|
CTCPListenSocket.cpp \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
|
targets: $(LIBTARGET)
|
||||||
|
|
||||||
|
$(LIBTARGET): $(OBJECTS)
|
||||||
|
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
|
||||||
|
$(ARF) $(LIBTARGET) $(OBJECTS)
|
|
@ -0,0 +1,84 @@
|
||||||
|
#include "XSocket.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// XSocketAddress
|
||||||
|
//
|
||||||
|
|
||||||
|
XSocketAddress::XSocketAddress(Error error,
|
||||||
|
const CString& hostname, SInt16 port) throw() :
|
||||||
|
m_error(error),
|
||||||
|
m_hostname(hostname),
|
||||||
|
m_port(port)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
XSocketAddress::Error XSocketAddress::getError() const throw()
|
||||||
|
{
|
||||||
|
return m_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString XSocketAddress::getHostname() const throw()
|
||||||
|
{
|
||||||
|
return m_hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
SInt16 XSocketAddress::getPort() const throw()
|
||||||
|
{
|
||||||
|
return m_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString XSocketAddress::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return "no address";
|
||||||
|
/*
|
||||||
|
return format("XSocketAddress", "no address: %1:%2",
|
||||||
|
m_hostname.t_str(),
|
||||||
|
CString::sprintf("%d", m_port).t_str());
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XSocketErrno
|
||||||
|
//
|
||||||
|
|
||||||
|
XSocketErrno::XSocketErrno() : MXErrno()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
XSocketErrno::XSocketErrno(int err) : MXErrno(err)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XSocketBind
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XSocketBind::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XSocketBind", "cannot bind address");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XSocketConnect
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XSocketConnect::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XSocketConnect", "cannot connect socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// XSocketCreate
|
||||||
|
//
|
||||||
|
|
||||||
|
CString XSocketCreate::getWhat() const throw()
|
||||||
|
{
|
||||||
|
return format("XSocketCreate", "cannot create socket");
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
#ifndef XSOCKET_H
|
||||||
|
#define XSOCKET_H
|
||||||
|
|
||||||
|
#include "CString.h"
|
||||||
|
#include "XBase.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
class XSocket : public XBase { };
|
||||||
|
|
||||||
|
class XSocketAddress : public XSocket {
|
||||||
|
public:
|
||||||
|
enum Error { kUnknown, kNotFound, kNoAddress, kBadPort };
|
||||||
|
|
||||||
|
XSocketAddress(Error, const CString& hostname, SInt16 port) throw();
|
||||||
|
|
||||||
|
// accessors
|
||||||
|
|
||||||
|
virtual Error getError() const throw();
|
||||||
|
virtual CString getHostname() const throw();
|
||||||
|
virtual SInt16 getPort() const throw();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Error m_error;
|
||||||
|
CString m_hostname;
|
||||||
|
SInt16 m_port;
|
||||||
|
};
|
||||||
|
|
||||||
|
class XSocketErrno : public XSocket, public MXErrno {
|
||||||
|
public:
|
||||||
|
XSocketErrno();
|
||||||
|
XSocketErrno(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
class XSocketBind : public XSocketErrno {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
class XSocketAddressInUse : public XSocketBind { };
|
||||||
|
|
||||||
|
class XSocketConnect : public XSocketErrno {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
class XSocketCreate : public XSocketErrno {
|
||||||
|
protected:
|
||||||
|
// XBase overrides
|
||||||
|
virtual CString getWhat() const throw();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue