Checkpointing centralized event queue stuff. Currently have:
an event queue and events, TCP sockets converted to use events, unix multithreading and network stuff converted, and an X Windows event queue subclass.
This commit is contained in:
parent
fa215f1b13
commit
4c7e524896
|
@ -357,6 +357,12 @@ CArch::waitForEvent(CArchThread thread, double timeout)
|
||||||
return m_mt->waitForEvent(thread, timeout);
|
return m_mt->waitForEvent(thread, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CArch::unblockThread(CArchThread thread)
|
||||||
|
{
|
||||||
|
m_mt->unblockThread(thread);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CArch::isSameThread(CArchThread thread1, CArchThread thread2)
|
CArch::isSameThread(CArchThread thread1, CArchThread thread2)
|
||||||
{
|
{
|
||||||
|
|
|
@ -117,6 +117,7 @@ public:
|
||||||
virtual void testCancelThread();
|
virtual void testCancelThread();
|
||||||
virtual bool wait(CArchThread, double timeout);
|
virtual bool wait(CArchThread, double timeout);
|
||||||
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
||||||
|
virtual void unblockThread(CArchThread thread);
|
||||||
virtual bool isSameThread(CArchThread, CArchThread);
|
virtual bool isSameThread(CArchThread, CArchThread);
|
||||||
virtual bool isExitedThread(CArchThread);
|
virtual bool isExitedThread(CArchThread);
|
||||||
virtual void* getResultOfThread(CArchThread);
|
virtual void* getResultOfThread(CArchThread);
|
||||||
|
@ -189,4 +190,20 @@ private:
|
||||||
IArchTime* m_time;
|
IArchTime* m_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Convenience object to lock/unlock an arch mutex
|
||||||
|
class CArchMutexLock {
|
||||||
|
public:
|
||||||
|
CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
|
||||||
|
{
|
||||||
|
ARCH->lockMutex(m_mutex);
|
||||||
|
}
|
||||||
|
~CArchMutexLock()
|
||||||
|
{
|
||||||
|
ARCH->unlockMutex(m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CArchMutex m_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -522,6 +522,12 @@ CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
|
||||||
return kTimeout;
|
return kTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CArchMultithreadPosix::unblockThread(CArchThread thread)
|
||||||
|
{
|
||||||
|
pthread_kill(thread->m_thread, SIGWAKEUP);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
|
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,7 @@ public:
|
||||||
virtual void testCancelThread();
|
virtual void testCancelThread();
|
||||||
virtual bool wait(CArchThread, double timeout);
|
virtual bool wait(CArchThread, double timeout);
|
||||||
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
||||||
|
virtual void unblockThread(CArchThread thread);
|
||||||
virtual bool isSameThread(CArchThread, CArchThread);
|
virtual bool isSameThread(CArchThread, CArchThread);
|
||||||
virtual bool isExitedThread(CArchThread);
|
virtual bool isExitedThread(CArchThread);
|
||||||
virtual void* getResultOfThread(CArchThread);
|
virtual void* getResultOfThread(CArchThread);
|
||||||
|
|
|
@ -200,11 +200,6 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr)
|
||||||
ARCH->testCancelThread();
|
ARCH->testCancelThread();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (err == ECONNABORTED) {
|
|
||||||
// connection was aborted; try again
|
|
||||||
ARCH->testCancelThread();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
delete newSocket;
|
delete newSocket;
|
||||||
delete *addr;
|
delete *addr;
|
||||||
*addr = NULL;
|
*addr = NULL;
|
||||||
|
@ -244,11 +239,6 @@ CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
// connecting
|
|
||||||
throw XArchNetworkConnecting(new XArchEvalUnix(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
throwError(errno);
|
throwError(errno);
|
||||||
}
|
}
|
||||||
} while (false);
|
} while (false);
|
||||||
|
@ -290,14 +280,15 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// do the poll
|
// do the poll
|
||||||
|
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
|
||||||
int n;
|
int n;
|
||||||
do {
|
do {
|
||||||
n = poll(pfd, num, static_cast<int>(1000.0 * timeout));
|
n = poll(pfd, num, t);
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
// interrupted system call
|
// interrupted system call
|
||||||
ARCH->testCancelThread();
|
ARCH->testCancelThread();
|
||||||
continue;
|
return 0;
|
||||||
}
|
}
|
||||||
throwError(errno);
|
throwError(errno);
|
||||||
}
|
}
|
||||||
|
@ -382,7 +373,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||||
// prepare timeout for select
|
// prepare timeout for select
|
||||||
struct timeval timeout2;
|
struct timeval timeout2;
|
||||||
struct timeval* timeout2P;
|
struct timeval* timeout2P;
|
||||||
if (timeout < 0) {
|
if (timeout < 0.0) {
|
||||||
timeout2P = NULL;
|
timeout2P = NULL;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -404,7 +395,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
// interrupted system call
|
// interrupted system call
|
||||||
ARCH->testCancelThread();
|
ARCH->testCancelThread();
|
||||||
continue;
|
return 0;
|
||||||
}
|
}
|
||||||
throwError(errno);
|
throwError(errno);
|
||||||
}
|
}
|
||||||
|
|
|
@ -228,6 +228,15 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
|
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
|
||||||
|
|
||||||
|
//! Unblock thread in system call
|
||||||
|
/*!
|
||||||
|
Cause a thread that's in a blocking system call to return. This
|
||||||
|
call may return before the thread is unblocked. If the thread is
|
||||||
|
not in a blocking system call, this call has no effect. This does
|
||||||
|
not cause a lockMutex() or waitCondVar() to return prematurely.
|
||||||
|
*/
|
||||||
|
virtual void unblockThread(CArchThread thread) = 0;
|
||||||
|
|
||||||
//! Compare threads
|
//! Compare threads
|
||||||
/*!
|
/*!
|
||||||
Returns true iff two thread objects refer to the same thread.
|
Returns true iff two thread objects refer to the same thread.
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CEvent.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CEvent
|
||||||
|
//
|
||||||
|
|
||||||
|
CEvent::Type CEvent::s_nextType = kLast;
|
||||||
|
|
||||||
|
CEvent::CEvent() :
|
||||||
|
m_type(kUnknown),
|
||||||
|
m_target(NULL),
|
||||||
|
m_data(NULL)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::CEvent(Type type, void* target, void* data) :
|
||||||
|
m_type(type),
|
||||||
|
m_target(target),
|
||||||
|
m_data(data)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
CEvent::getType() const
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
CEvent::getTarget() const
|
||||||
|
{
|
||||||
|
return m_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
CEvent::getData() const
|
||||||
|
{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
CEvent::registerType()
|
||||||
|
{
|
||||||
|
// FIXME -- lock mutex (need a mutex)
|
||||||
|
return s_nextType++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
CEvent::registerTypeOnce(Type& type)
|
||||||
|
{
|
||||||
|
// FIXME -- lock mutex (need a mutex)
|
||||||
|
if (type == CEvent::kUnknown) {
|
||||||
|
type = s_nextType++;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEvent::deleteData(const CEvent& event)
|
||||||
|
{
|
||||||
|
switch (event.getType()) {
|
||||||
|
case kUnknown:
|
||||||
|
case kQuit:
|
||||||
|
case kSystem:
|
||||||
|
case kTimer:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// yes, really delete void*
|
||||||
|
delete event.getData();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CEVENT_H
|
||||||
|
#define CEVENT_H
|
||||||
|
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
//! Event
|
||||||
|
/*!
|
||||||
|
A \c CEvent holds an event type and a pointer to event data.
|
||||||
|
*/
|
||||||
|
class CEvent {
|
||||||
|
public:
|
||||||
|
typedef UInt32 Type;
|
||||||
|
enum {
|
||||||
|
kUnknown, //!< The event type is unknown
|
||||||
|
kQuit, //!< The quit event
|
||||||
|
kSystem, //!< The data points to a system event type
|
||||||
|
kTimer, //!< The data points to timer info
|
||||||
|
kLast //!< Must be last
|
||||||
|
};
|
||||||
|
|
||||||
|
CEvent();
|
||||||
|
|
||||||
|
//! Create \c CEvent with data
|
||||||
|
/*!
|
||||||
|
The \p type must have been registered using \c registerType().
|
||||||
|
The \p data must be POD (plain old data) which means it cannot
|
||||||
|
have a destructor or be composed of any types that do. \p target
|
||||||
|
is the intended recipient of the event.
|
||||||
|
*/
|
||||||
|
CEvent(Type type, void* target = NULL, void* data = NULL);
|
||||||
|
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get event type
|
||||||
|
/*!
|
||||||
|
Returns the event type.
|
||||||
|
*/
|
||||||
|
Type getType() const;
|
||||||
|
|
||||||
|
//! Get the event target
|
||||||
|
/*!
|
||||||
|
Returns the event target.
|
||||||
|
*/
|
||||||
|
void* getTarget() const;
|
||||||
|
|
||||||
|
//! Get the event data
|
||||||
|
/*!
|
||||||
|
Returns the event data.
|
||||||
|
*/
|
||||||
|
void* getData() const;
|
||||||
|
|
||||||
|
//! Creates a new event type
|
||||||
|
/*!
|
||||||
|
Returns a unique event type id.
|
||||||
|
*/
|
||||||
|
static Type registerType();
|
||||||
|
|
||||||
|
//! Creates a new event type
|
||||||
|
/*!
|
||||||
|
If \p type contains \c kUnknown then it is set to a unique event
|
||||||
|
type id otherwise it is left alone. The final value of \p type
|
||||||
|
is returned.
|
||||||
|
*/
|
||||||
|
static Type registerTypeOnce(Type& type);
|
||||||
|
|
||||||
|
//! Release event data
|
||||||
|
/*!
|
||||||
|
Deletes event data for the given event.
|
||||||
|
*/
|
||||||
|
static void deleteData(const CEvent&);
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type m_type;
|
||||||
|
void* m_target;
|
||||||
|
void* m_data;
|
||||||
|
static Type s_nextType;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,381 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CEventQueue.h"
|
||||||
|
#include "IEventJob.h"
|
||||||
|
#include "CArch.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CEventQueue
|
||||||
|
//
|
||||||
|
|
||||||
|
static int g_systemTarget = 0;
|
||||||
|
CEventQueue* CEventQueue::s_instance = NULL;
|
||||||
|
|
||||||
|
CEventQueue::CEventQueue()
|
||||||
|
{
|
||||||
|
assert(s_instance == NULL);
|
||||||
|
s_instance = this;
|
||||||
|
m_mutex = ARCH->newMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueue::~CEventQueue()
|
||||||
|
{
|
||||||
|
ARCH->closeMutex(m_mutex);
|
||||||
|
s_instance = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
CEventQueue::getSystemTarget()
|
||||||
|
{
|
||||||
|
// any unique arbitrary pointer will do
|
||||||
|
return &g_systemTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueue*
|
||||||
|
CEventQueue::getInstance()
|
||||||
|
{
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::getEvent(CEvent& event, double timeout)
|
||||||
|
{
|
||||||
|
// if no events are waiting then handle timers and then wait
|
||||||
|
if (doIsEmpty()) {
|
||||||
|
// handle timers first
|
||||||
|
if (hasTimerExpired(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get time until next timer expires. if there is a timer
|
||||||
|
// and it'll expire before the client's timeout then use
|
||||||
|
// that duration for our timeout instead.
|
||||||
|
double timerTimeout = getNextTimerTimeout();
|
||||||
|
if (timerTimeout >= 0.0 && timerTimeout < timeout) {
|
||||||
|
timeout = timerTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for an event
|
||||||
|
waitForEvent(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no events are pending then do the timers
|
||||||
|
if (doIsEmpty()) {
|
||||||
|
return hasTimerExpired(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doGetEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::dispatchEvent(const CEvent& event)
|
||||||
|
{
|
||||||
|
void* target = event.getTarget();
|
||||||
|
IEventJob* job = getHandler(target);
|
||||||
|
if (job != NULL) {
|
||||||
|
job->run(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEventQueue::addEvent(const CEvent& event)
|
||||||
|
{
|
||||||
|
// discard bogus event types
|
||||||
|
switch (event.getType()) {
|
||||||
|
case CEvent::kUnknown:
|
||||||
|
case CEvent::kSystem:
|
||||||
|
case CEvent::kTimer:
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the event's data locally
|
||||||
|
UInt32 eventID = saveEvent(event);
|
||||||
|
|
||||||
|
// add it
|
||||||
|
if (!doAddEvent(eventID)) {
|
||||||
|
// failed to send event
|
||||||
|
removeEvent(eventID);
|
||||||
|
CEvent::deleteData(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueueTimer*
|
||||||
|
CEventQueue::newTimer(double duration, void* target)
|
||||||
|
{
|
||||||
|
assert(duration > 0.0);
|
||||||
|
|
||||||
|
CEventQueueTimer* timer = doNewTimer(duration, false);
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
m_timers.insert(timer);
|
||||||
|
m_timerQueue.push(CTimer(timer, duration, target, false));
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueueTimer*
|
||||||
|
CEventQueue::newOneShotTimer(double duration, void* target)
|
||||||
|
{
|
||||||
|
assert(duration > 0.0);
|
||||||
|
|
||||||
|
CEventQueueTimer* timer = doNewTimer(duration, true);
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
m_timers.insert(timer);
|
||||||
|
m_timerQueue.push(CTimer(timer, duration, target, true));
|
||||||
|
return timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEventQueue::deleteTimer(CEventQueueTimer* timer)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
for (CTimerQueue::iterator index = m_timerQueue.begin();
|
||||||
|
index != m_timerQueue.end(); ++index) {
|
||||||
|
if (index->getTimer() == timer) {
|
||||||
|
m_timerQueue.erase(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CTimers::iterator index = m_timers.find(timer);
|
||||||
|
if (index != m_timers.end()) {
|
||||||
|
m_timers.erase(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doDeleteTimer(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEventQueue::adoptHandler(void* target, IEventJob* handler)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
IEventJob*& job = m_handlers[target];
|
||||||
|
delete job;
|
||||||
|
job = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEventJob*
|
||||||
|
CEventQueue::orphanHandler(void* target)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
CHandlerTable::iterator index = m_handlers.find(target);
|
||||||
|
if (index != m_handlers.end()) {
|
||||||
|
IEventJob* handler = index->second;
|
||||||
|
m_handlers.erase(index);
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::isEmpty() const
|
||||||
|
{
|
||||||
|
return (doIsEmpty() && getNextTimerTimeout() != 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEventJob*
|
||||||
|
CEventQueue::getHandler(void* target) const
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
CHandlerTable::const_iterator index = m_handlers.find(target);
|
||||||
|
if (index != m_handlers.end()) {
|
||||||
|
return index->second;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UInt32
|
||||||
|
CEventQueue::saveEvent(const CEvent& event)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
|
||||||
|
// choose id
|
||||||
|
UInt32 id;
|
||||||
|
if (!m_oldEventIDs.empty()) {
|
||||||
|
// reuse an id
|
||||||
|
id = m_oldEventIDs.back();
|
||||||
|
m_oldEventIDs.pop_back();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// make a new id
|
||||||
|
id = static_cast<UInt32>(m_oldEventIDs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// save data
|
||||||
|
m_events[id] = event;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent
|
||||||
|
CEventQueue::removeEvent(UInt32 eventID)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
|
||||||
|
// look up id
|
||||||
|
CEventTable::iterator index = m_events.find(eventID);
|
||||||
|
if (index == m_events.end()) {
|
||||||
|
return CEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// get data
|
||||||
|
CEvent event = index->second;
|
||||||
|
m_events.erase(index);
|
||||||
|
|
||||||
|
// save old id for reuse
|
||||||
|
m_oldEventIDs.push_back(eventID);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::hasTimerExpired(CEvent& event)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
|
||||||
|
// return true if there's a timer in the timer priority queue that
|
||||||
|
// has expired. if returning true then fill in event appropriately
|
||||||
|
// and reset and reinsert the timer.
|
||||||
|
if (m_timerQueue.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get time elapsed since last check
|
||||||
|
const double time = m_time.getTime();
|
||||||
|
m_time.reset();
|
||||||
|
|
||||||
|
// countdown elapsed time
|
||||||
|
for (CTimerQueue::iterator index = m_timerQueue.begin();
|
||||||
|
index != m_timerQueue.end(); ++index) {
|
||||||
|
(*index) -= time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// done if no timers are expired
|
||||||
|
if (m_timerQueue.top() > 0.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove timer from queue
|
||||||
|
CTimer timer = m_timerQueue.top();
|
||||||
|
m_timerQueue.pop();
|
||||||
|
|
||||||
|
// prepare event and reset the timer's clock
|
||||||
|
timer.fillEvent(m_timerEvent);
|
||||||
|
event = CEvent(CEvent::kTimer, timer.getTarget(), &m_timerEvent);
|
||||||
|
timer.reset();
|
||||||
|
|
||||||
|
// reinsert timer into queue if it's not a one-shot
|
||||||
|
if (!timer.isOneShot()) {
|
||||||
|
m_timerQueue.push(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double
|
||||||
|
CEventQueue::getNextTimerTimeout() const
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_mutex);
|
||||||
|
|
||||||
|
// return -1 if no timers, 0 if the top timer has expired, otherwise
|
||||||
|
// the time until the top timer in the timer priority queue will
|
||||||
|
// expire.
|
||||||
|
if (m_timerQueue.empty()) {
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
if (m_timerQueue.top() <= 0.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
return m_timerQueue.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// CXWindowsScreen::CTimer
|
||||||
|
//
|
||||||
|
|
||||||
|
CEventQueue::CTimer::CTimer(CEventQueueTimer* timer,
|
||||||
|
double timeout, void* target, bool oneShot) :
|
||||||
|
m_timer(timer),
|
||||||
|
m_timeout(timeout),
|
||||||
|
m_target(target),
|
||||||
|
m_oneShot(oneShot),
|
||||||
|
m_time(timeout)
|
||||||
|
{
|
||||||
|
assert(m_timeout > 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueue::CTimer::~CTimer()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEventQueue::CTimer::reset()
|
||||||
|
{
|
||||||
|
m_time = m_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueue::CTimer::CTimer&
|
||||||
|
CEventQueue::CTimer::operator-=(double dt)
|
||||||
|
{
|
||||||
|
m_time -= dt;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueue::CTimer::operator double() const
|
||||||
|
{
|
||||||
|
return m_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::CTimer::isOneShot() const
|
||||||
|
{
|
||||||
|
return m_oneShot;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueueTimer*
|
||||||
|
CEventQueue::CTimer::getTimer() const
|
||||||
|
{
|
||||||
|
return m_timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
CEventQueue::CTimer::getTarget() const
|
||||||
|
{
|
||||||
|
return m_target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CEventQueue::CTimer::fillEvent(CTimerEvent& event) const
|
||||||
|
{
|
||||||
|
event.m_timer = m_timer;
|
||||||
|
event.m_count = 0;
|
||||||
|
if (m_time <= 0.0) {
|
||||||
|
event.m_count = static_cast<UInt32>((m_timeout - m_time) / m_timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CEventQueue::CTimer::operator<(const CTimer& t) const
|
||||||
|
{
|
||||||
|
return m_time < t.m_time;
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CEVENTQUEUE_H
|
||||||
|
#define CEVENTQUEUE_H
|
||||||
|
|
||||||
|
#include "IEventQueue.h"
|
||||||
|
#include "CEvent.h"
|
||||||
|
#include "CPriorityQueue.h"
|
||||||
|
#include "CStopwatch.h"
|
||||||
|
#include "IArchMultithread.h"
|
||||||
|
#include "stdmap.h"
|
||||||
|
#include "stdset.h"
|
||||||
|
|
||||||
|
//! Event queue
|
||||||
|
/*!
|
||||||
|
An event queue that implements the platform independent parts and
|
||||||
|
delegates the platform dependent parts to a subclass.
|
||||||
|
*/
|
||||||
|
class CEventQueue : public IEventQueue {
|
||||||
|
public:
|
||||||
|
CEventQueue();
|
||||||
|
virtual ~CEventQueue();
|
||||||
|
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get the system event type target
|
||||||
|
/*!
|
||||||
|
Returns the target to use for dispatching \c CEvent::kSystem events.
|
||||||
|
*/
|
||||||
|
static void* getSystemTarget();
|
||||||
|
|
||||||
|
//! Get the singleton instance
|
||||||
|
/*!
|
||||||
|
Returns the singleton instance of the event queue
|
||||||
|
*/
|
||||||
|
static CEventQueue* getInstance();
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
// IEventQueue overrides
|
||||||
|
virtual bool getEvent(CEvent& event, double timeout = -1.0);
|
||||||
|
virtual bool dispatchEvent(const CEvent& event);
|
||||||
|
virtual void addEvent(const CEvent& event);
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
newTimer(double duration, void* target = NULL);
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
newOneShotTimer(double duration, void* target = NULL);
|
||||||
|
virtual void deleteTimer(CEventQueueTimer*);
|
||||||
|
virtual void adoptHandler(void* target, IEventJob* dispatcher);
|
||||||
|
virtual IEventJob* orphanHandler(void* target);
|
||||||
|
virtual bool isEmpty() const;
|
||||||
|
virtual IEventJob* getHandler(void* target) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get the data for a given id
|
||||||
|
/*!
|
||||||
|
Takes a saved event id, \p eventID, and returns a \c CEvent. The
|
||||||
|
event id becomes invalid after this call. The \p eventID must have
|
||||||
|
been passed to a successful call to \c doAddEvent() and not removed
|
||||||
|
since.
|
||||||
|
*/
|
||||||
|
CEvent removeEvent(UInt32 eventID);
|
||||||
|
|
||||||
|
//! Block waiting for an event
|
||||||
|
/*!
|
||||||
|
Wait for an event in the system event queue for up to \p timeout
|
||||||
|
seconds.
|
||||||
|
*/
|
||||||
|
virtual void waitForEvent(double timeout) = 0;
|
||||||
|
|
||||||
|
//! Get the next event
|
||||||
|
/*!
|
||||||
|
Remove the next system event (one should be pending) and convert it
|
||||||
|
to a \c CEvent. The event type should be either \c CEvent::kSystem
|
||||||
|
if the event was not added by \c doAddEvent() or a type returned by
|
||||||
|
\c CEvent::registerType() if it was (and not \c CEvent::kTimer). A
|
||||||
|
non-system event will normally be retrieved by \c removeEvent(), but
|
||||||
|
the implementation must be able to tell the difference between a
|
||||||
|
system event and one added by \c doAddEvent().
|
||||||
|
*/
|
||||||
|
virtual bool doGetEvent(CEvent& event) = 0;
|
||||||
|
|
||||||
|
//! Post an event
|
||||||
|
/*!
|
||||||
|
Add the given event to the end of the system queue. This is a user
|
||||||
|
event and \c doGetEvent() must be able to identify it as such.
|
||||||
|
This method must cause \c waitForEvent() to return at some future
|
||||||
|
time if it's blocked waiting on an event.
|
||||||
|
*/
|
||||||
|
virtual bool doAddEvent(UInt32 dataID) = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Check if system queue is empty
|
||||||
|
/*!
|
||||||
|
Return true iff the system queue is empty.
|
||||||
|
*/
|
||||||
|
virtual bool doIsEmpty() const = 0;
|
||||||
|
|
||||||
|
//! Create a timer object
|
||||||
|
/*!
|
||||||
|
Create and return a timer object. The object is opaque and is
|
||||||
|
used only by the subclass but it must be a valid object (i.e.
|
||||||
|
not NULL).
|
||||||
|
*/
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
doNewTimer(double duration, bool oneShot) const = 0;
|
||||||
|
|
||||||
|
//! Destroy a timer object
|
||||||
|
/*!
|
||||||
|
Destroy a timer object previously returned by \c doNewTimer().
|
||||||
|
*/
|
||||||
|
virtual void doDeleteTimer(CEventQueueTimer*) const = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UInt32 saveEvent(const CEvent& event);
|
||||||
|
bool hasTimerExpired(CEvent& event);
|
||||||
|
double getNextTimerTimeout() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class CTimer {
|
||||||
|
public:
|
||||||
|
CTimer(CEventQueueTimer*, double timeout, void* target, bool oneShot);
|
||||||
|
~CTimer();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
CTimer& operator-=(double);
|
||||||
|
|
||||||
|
operator double() const;
|
||||||
|
|
||||||
|
bool isOneShot() const;
|
||||||
|
CEventQueueTimer*
|
||||||
|
getTimer() const;
|
||||||
|
void* getTarget() const;
|
||||||
|
void fillEvent(CTimerEvent&) const;
|
||||||
|
|
||||||
|
bool operator<(const CTimer&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CEventQueueTimer* m_timer;
|
||||||
|
double m_timeout;
|
||||||
|
void* m_target;
|
||||||
|
bool m_oneShot;
|
||||||
|
double m_time;
|
||||||
|
};
|
||||||
|
typedef std::set<CEventQueueTimer*> CTimers;
|
||||||
|
typedef CPriorityQueue<CTimer> CTimerQueue;
|
||||||
|
typedef std::map<UInt32, CEvent> CEventTable;
|
||||||
|
typedef std::vector<UInt32> CEventIDList;
|
||||||
|
typedef std::map<void*, IEventJob*> CHandlerTable;
|
||||||
|
|
||||||
|
static CEventQueue* s_instance;
|
||||||
|
|
||||||
|
CArchMutex m_mutex;
|
||||||
|
|
||||||
|
CEventTable m_events;
|
||||||
|
CEventIDList m_oldEventIDs;
|
||||||
|
|
||||||
|
CStopwatch m_time;
|
||||||
|
CTimers m_timers;
|
||||||
|
CTimerQueue m_timerQueue;
|
||||||
|
CTimerEvent m_timerEvent;
|
||||||
|
|
||||||
|
CHandlerTable m_handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CFunctionEventJob.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// CFunctionJob
|
||||||
|
//
|
||||||
|
|
||||||
|
CFunctionEventJob::CFunctionEventJob(
|
||||||
|
void (*func)(const CEvent&, void*), void* arg) :
|
||||||
|
m_func(func),
|
||||||
|
m_arg(arg)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
CFunctionEventJob::~CFunctionEventJob()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CFunctionEventJob::run(const CEvent& event)
|
||||||
|
{
|
||||||
|
if (m_func != NULL) {
|
||||||
|
m_func(event, m_arg);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CFUNCTIONEVENTJOB_H
|
||||||
|
#define CFUNCTIONEVENTJOB_H
|
||||||
|
|
||||||
|
#include "IEventJob.h"
|
||||||
|
|
||||||
|
//! Use a function as an event job
|
||||||
|
/*!
|
||||||
|
An event job class that invokes a function.
|
||||||
|
*/
|
||||||
|
class CFunctionEventJob : public IEventJob {
|
||||||
|
public:
|
||||||
|
//! run() invokes \c func(arg)
|
||||||
|
CFunctionEventJob(void (*func)(const CEvent&, void*), void* arg = NULL);
|
||||||
|
virtual ~CFunctionEventJob();
|
||||||
|
|
||||||
|
// IEventJob overrides
|
||||||
|
virtual void run(const CEvent&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void (*m_func)(const CEvent&, void*);
|
||||||
|
void* m_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -54,16 +54,6 @@ static const int g_prioritySuffixLength = 2;
|
||||||
static const int g_priorityPad = g_maxPriorityLength +
|
static const int g_priorityPad = g_maxPriorityLength +
|
||||||
g_prioritySuffixLength;
|
g_prioritySuffixLength;
|
||||||
|
|
||||||
//! Convenience object to lock/unlock a mutex
|
|
||||||
class CLogLock {
|
|
||||||
public:
|
|
||||||
CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); }
|
|
||||||
~CLogLock() { ARCH->unlockMutex(m_mutex); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
CArchMutex m_mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// CLog
|
// CLog
|
||||||
|
@ -198,7 +188,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
|
||||||
assert(outputter != NULL);
|
assert(outputter != NULL);
|
||||||
assert(outputter->getNewline() != NULL);
|
assert(outputter->getNewline() != NULL);
|
||||||
|
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
if (alwaysAtHead) {
|
if (alwaysAtHead) {
|
||||||
m_alwaysOutputters.push_front(outputter);
|
m_alwaysOutputters.push_front(outputter);
|
||||||
}
|
}
|
||||||
|
@ -214,7 +204,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
|
||||||
void
|
void
|
||||||
CLog::remove(ILogOutputter* outputter)
|
CLog::remove(ILogOutputter* outputter)
|
||||||
{
|
{
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
m_outputters.remove(outputter);
|
m_outputters.remove(outputter);
|
||||||
m_alwaysOutputters.remove(outputter);
|
m_alwaysOutputters.remove(outputter);
|
||||||
}
|
}
|
||||||
|
@ -222,7 +212,7 @@ CLog::remove(ILogOutputter* outputter)
|
||||||
void
|
void
|
||||||
CLog::pop_front(bool alwaysAtHead)
|
CLog::pop_front(bool alwaysAtHead)
|
||||||
{
|
{
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
|
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
|
||||||
if (!list->empty()) {
|
if (!list->empty()) {
|
||||||
delete list->front();
|
delete list->front();
|
||||||
|
@ -248,14 +238,14 @@ CLog::setFilter(const char* maxPriority)
|
||||||
void
|
void
|
||||||
CLog::setFilter(int maxPriority)
|
CLog::setFilter(int maxPriority)
|
||||||
{
|
{
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
m_maxPriority = maxPriority;
|
m_maxPriority = maxPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CLog::getFilter() const
|
CLog::getFilter() const
|
||||||
{
|
{
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
return m_maxPriority;
|
return m_maxPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,7 +266,7 @@ CLog::output(int priority, char* msg) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to each outputter
|
// write to each outputter
|
||||||
CLogLock lock(m_mutex);
|
CArchMutexLock lock(m_mutex);
|
||||||
for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
|
for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
|
||||||
index != m_alwaysOutputters.end();
|
index != m_alwaysOutputters.end();
|
||||||
++index) {
|
++index) {
|
||||||
|
|
|
@ -56,6 +56,13 @@ public:
|
||||||
c.pop_back();
|
c.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! Erase element
|
||||||
|
void erase(iterator i)
|
||||||
|
{
|
||||||
|
c.erase(i);
|
||||||
|
std::make_heap(c.begin(), c.end(), comp);
|
||||||
|
}
|
||||||
|
|
||||||
//! Get start iterator
|
//! Get start iterator
|
||||||
iterator begin()
|
iterator begin()
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CSimpleEventQueue.h"
|
||||||
|
#include "CArch.h"
|
||||||
|
|
||||||
|
class CEventQueueTimer { };
|
||||||
|
|
||||||
|
//
|
||||||
|
// CSimpleEventQueue
|
||||||
|
//
|
||||||
|
|
||||||
|
CSimpleEventQueue::CSimpleEventQueue()
|
||||||
|
{
|
||||||
|
m_queueMutex = ARCH->newMutex();
|
||||||
|
m_queueReadyCond = ARCH->newCondVar();
|
||||||
|
m_queueReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSimpleEventQueue::~CSimpleEventQueue()
|
||||||
|
{
|
||||||
|
ARCH->closeCondVar(m_queueReadyCond);
|
||||||
|
ARCH->closeMutex(m_queueMutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CSimpleEventQueue::waitForEvent(double timeout)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_queueMutex);
|
||||||
|
while (!m_queueReady) {
|
||||||
|
ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, -1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CSimpleEventQueue::doGetEvent(CEvent& event)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_queueMutex);
|
||||||
|
if (!m_queueReady) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
event = removeEvent(m_queue.back());
|
||||||
|
m_queue.pop_back();
|
||||||
|
m_queueReady = !m_queue.empty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CSimpleEventQueue::doAddEvent(UInt32 dataID)
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_queueMutex);
|
||||||
|
m_queue.push_front(dataID);
|
||||||
|
if (!m_queueReady) {
|
||||||
|
m_queueReady = true;
|
||||||
|
ARCH->broadcastCondVar(m_queueReadyCond);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CSimpleEventQueue::doIsEmpty() const
|
||||||
|
{
|
||||||
|
CArchMutexLock lock(m_queueMutex);
|
||||||
|
return !m_queueReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueueTimer*
|
||||||
|
CSimpleEventQueue::doNewTimer(double, bool) const
|
||||||
|
{
|
||||||
|
return new CEventQueueTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CSimpleEventQueue::doDeleteTimer(CEventQueueTimer* timer) const
|
||||||
|
{
|
||||||
|
delete timer;
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CSIMPLEEVENTQUEUE_H
|
||||||
|
#define CSIMPLEEVENTQUEUE_H
|
||||||
|
|
||||||
|
#include "CEventQueue.h"
|
||||||
|
#include "IArchMultithread.h"
|
||||||
|
#include "stddeque.h"
|
||||||
|
|
||||||
|
//! Event queue for added events only
|
||||||
|
/*!
|
||||||
|
An event queue that provides no system events, just events added by
|
||||||
|
addEvent().
|
||||||
|
*/
|
||||||
|
class CSimpleEventQueue : public CEventQueue {
|
||||||
|
public:
|
||||||
|
CSimpleEventQueue();
|
||||||
|
virtual ~CSimpleEventQueue();
|
||||||
|
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// CEventQueue overrides
|
||||||
|
virtual void waitForEvent(double timeout);
|
||||||
|
virtual bool doGetEvent(CEvent& event);
|
||||||
|
virtual bool doAddEvent(UInt32 dataID);
|
||||||
|
virtual bool doIsEmpty() const;
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
doNewTimer(double duration, bool oneShot) const;
|
||||||
|
virtual void doDeleteTimer(CEventQueueTimer*) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::deque<UInt32> CEventDeque;
|
||||||
|
|
||||||
|
CArchMutex m_queueMutex;
|
||||||
|
CArchCond m_queueReadyCond;
|
||||||
|
bool m_queueReady;
|
||||||
|
CEventDeque m_queue;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IEVENTJOB_H
|
||||||
|
#define IEVENTJOB_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
|
||||||
|
class CEvent;
|
||||||
|
|
||||||
|
//! Event handler interface
|
||||||
|
/*!
|
||||||
|
An event job is an interface for executing a event handler.
|
||||||
|
*/
|
||||||
|
class IEventJob : public IInterface {
|
||||||
|
public:
|
||||||
|
//! Run the job
|
||||||
|
virtual void run(const CEvent&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IEVENTQUEUE_H
|
||||||
|
#define IEVENTQUEUE_H
|
||||||
|
|
||||||
|
#include "IInterface.h"
|
||||||
|
#include "BasicTypes.h"
|
||||||
|
|
||||||
|
class CEvent;
|
||||||
|
class IEventJob;
|
||||||
|
|
||||||
|
// Opaque type for timer info. This is defined by subclasses of
|
||||||
|
// IEventQueue.
|
||||||
|
class CEventQueueTimer;
|
||||||
|
|
||||||
|
//! Event queue interface
|
||||||
|
/*!
|
||||||
|
An event queue provides a queue of CEvents. Clients can block waiting
|
||||||
|
on any event becoming available at the head of the queue and can place
|
||||||
|
new events at the end of the queue. Clients can also add and remove
|
||||||
|
timers which generate events periodically.
|
||||||
|
*/
|
||||||
|
class IEventQueue : public IInterface {
|
||||||
|
public:
|
||||||
|
class CTimerEvent {
|
||||||
|
public:
|
||||||
|
CEventQueueTimer* m_timer; //!< The timer
|
||||||
|
UInt32 m_count; //!< Number of repeats
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Remove event from queue
|
||||||
|
/*!
|
||||||
|
Returns the next event on the queue into \p event. If no event is
|
||||||
|
available then blocks for up to \p timeout seconds, or forever if
|
||||||
|
\p timeout is negative. Returns true iff an event was available.
|
||||||
|
*/
|
||||||
|
virtual bool getEvent(CEvent& event, double timeout = -1.0) = 0;
|
||||||
|
|
||||||
|
//! Dispatch an event
|
||||||
|
/*!
|
||||||
|
Looks up the dispatcher for the event's target and invokes it.
|
||||||
|
Returns true iff a dispatcher exists for the target.
|
||||||
|
*/
|
||||||
|
virtual bool dispatchEvent(const CEvent& event) = 0;
|
||||||
|
|
||||||
|
//! Add event to queue
|
||||||
|
/*!
|
||||||
|
Adds \p event to the end of the queue.
|
||||||
|
*/
|
||||||
|
virtual void addEvent(const CEvent& event) = 0;
|
||||||
|
|
||||||
|
//! Create a recurring timer
|
||||||
|
/*!
|
||||||
|
Creates and returns a timer. An event is returned after \p duration
|
||||||
|
seconds and the timer is reset to countdown again. When a timer event
|
||||||
|
is returned the data points to a \c CTimerEvent. The client must pass
|
||||||
|
the returned timer to \c deleteTimer() (whether or not the timer has
|
||||||
|
expired) to release the timer. The returned timer event uses the
|
||||||
|
given \p target.
|
||||||
|
|
||||||
|
Events for a single timer don't accumulate in the queue, even if the
|
||||||
|
client reading events can't keep up. Instead, the \c m_count member
|
||||||
|
of the \c CTimerEvent indicates how many events for the timer would
|
||||||
|
have been put on the queue since the last event for the timer was
|
||||||
|
removed (or since the timer was added).
|
||||||
|
*/
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
newTimer(double duration, void* target = NULL) = 0;
|
||||||
|
|
||||||
|
//! Create a one-shot timer
|
||||||
|
/*!
|
||||||
|
Creates and returns a one-shot timer. An event is returned when
|
||||||
|
the timer expires and the timer is removed from further handling.
|
||||||
|
When a timer event is returned the data points to a \c CTimerEvent.
|
||||||
|
The \m c_count member of the \c CTimerEvent is always 1. The client
|
||||||
|
must pass the returned timer to \c deleteTimer() (whether or not the
|
||||||
|
timer has expired) to release the timer. The returned timer event
|
||||||
|
uses the given \p target.
|
||||||
|
*/
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
newOneShotTimer(double duration,
|
||||||
|
void* target = NULL) = 0;
|
||||||
|
|
||||||
|
//! Destroy a timer
|
||||||
|
/*!
|
||||||
|
Destroys a previously created timer. The timer is removed from the
|
||||||
|
queue and will not generate event, even if the timer has expired.
|
||||||
|
*/
|
||||||
|
virtual void deleteTimer(CEventQueueTimer*) = 0;
|
||||||
|
|
||||||
|
//! Register an event handler
|
||||||
|
/*!
|
||||||
|
Registers an event handler for \p target. The \p handler is
|
||||||
|
adopted. Any existing handler for the target is deleted.
|
||||||
|
*/
|
||||||
|
virtual void adoptHandler(void* target, IEventJob* handler) = 0;
|
||||||
|
|
||||||
|
//! Unregister an event handler
|
||||||
|
/*!
|
||||||
|
Unregisters an event handler for \p target and returns it.
|
||||||
|
Returns NULL if there was no such handler. The client becomes
|
||||||
|
responsible for deleting the returned handler.
|
||||||
|
*/
|
||||||
|
virtual IEventJob* orphanHandler(void* target) = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Test if queue is empty
|
||||||
|
/*!
|
||||||
|
Returns true iff the queue has no events in it, including timer
|
||||||
|
events.
|
||||||
|
*/
|
||||||
|
virtual bool isEmpty() const = 0;
|
||||||
|
|
||||||
|
//! Get an event handler
|
||||||
|
/*!
|
||||||
|
Finds and returns the event handler for \p target, or NULL if
|
||||||
|
there is no such handler.
|
||||||
|
*/
|
||||||
|
virtual IEventJob* getHandler(void* target) const = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -25,25 +25,34 @@ MAINTAINERCLEANFILES = \
|
||||||
|
|
||||||
noinst_LIBRARIES = libbase.a
|
noinst_LIBRARIES = libbase.a
|
||||||
libbase_a_SOURCES = \
|
libbase_a_SOURCES = \
|
||||||
|
CEvent.cpp \
|
||||||
|
CEventQueue.cpp \
|
||||||
|
CFunctionEventJob.cpp \
|
||||||
CFunctionJob.cpp \
|
CFunctionJob.cpp \
|
||||||
CJobList.cpp \
|
CJobList.cpp \
|
||||||
CLog.cpp \
|
CLog.cpp \
|
||||||
|
CSimpleEventQueue.cpp \
|
||||||
CStopwatch.cpp \
|
CStopwatch.cpp \
|
||||||
CStringUtil.cpp \
|
CStringUtil.cpp \
|
||||||
CUnicode.cpp \
|
CUnicode.cpp \
|
||||||
LogOutputters.cpp \
|
LogOutputters.cpp \
|
||||||
XBase.cpp \
|
XBase.cpp \
|
||||||
|
CEvent.h \
|
||||||
|
CEventQueue.h \
|
||||||
CFunctionJob.h \
|
CFunctionJob.h \
|
||||||
CJobList.h \
|
CJobList.h \
|
||||||
CLog.h \
|
CLog.h \
|
||||||
CPriorityQueue.h \
|
CPriorityQueue.h \
|
||||||
|
CSimpleEventQueue.h \
|
||||||
CStopwatch.h \
|
CStopwatch.h \
|
||||||
CString.h \
|
CString.h \
|
||||||
CStringUtil.h \
|
CStringUtil.h \
|
||||||
CUnicode.h \
|
CUnicode.h \
|
||||||
|
IEventQueue.h \
|
||||||
IJob.h \
|
IJob.h \
|
||||||
ILogOutputter.h \
|
ILogOutputter.h \
|
||||||
LogOutputters.h \
|
LogOutputters.h \
|
||||||
|
TMethodEventJob.h \
|
||||||
TMethodJob.h \
|
TMethodJob.h \
|
||||||
XBase.h \
|
XBase.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CMETHODEVENTJOB_H
|
||||||
|
#define CMETHODEVENTJOB_H
|
||||||
|
|
||||||
|
#include "IEventJob.h"
|
||||||
|
|
||||||
|
//! Use a member function as an event job
|
||||||
|
/*!
|
||||||
|
An event job class that invokes a member function.
|
||||||
|
*/
|
||||||
|
template <class T>
|
||||||
|
class TMethodEventJob : public IEventJob {
|
||||||
|
public:
|
||||||
|
//! run(event) invokes \c object->method(event, arg)
|
||||||
|
TMethodEventJob(T* object,
|
||||||
|
void (T::*method)(const CEvent&, void*),
|
||||||
|
void* arg = NULL);
|
||||||
|
virtual ~TMethodEventJob();
|
||||||
|
|
||||||
|
// IJob overrides
|
||||||
|
virtual void run(const CEvent&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
T* m_object;
|
||||||
|
void (T::*m_method)(const CEvent&, void*);
|
||||||
|
void* m_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
TMethodEventJob<T>::TMethodEventJob(T* object,
|
||||||
|
void (T::*method)(const CEvent&, void*), void* arg) :
|
||||||
|
m_object(object),
|
||||||
|
m_method(method),
|
||||||
|
m_arg(arg)
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
TMethodEventJob<T>::~TMethodEventJob()
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
TMethodEventJob<T>::run(const CEvent& event)
|
||||||
|
{
|
||||||
|
if (m_object != NULL) {
|
||||||
|
(m_object->*m_method)(event, m_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -26,9 +26,10 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
CBufferedInputStream::CBufferedInputStream(
|
CBufferedInputStream::CBufferedInputStream(
|
||||||
CMutex* mutex, IJob* adoptedCloseCB) :
|
CMutex* mutex, IJob* adoptedEmptyCB, IJob* adoptedCloseCB) :
|
||||||
m_mutex(mutex),
|
m_mutex(mutex),
|
||||||
m_empty(mutex, true),
|
m_empty(mutex, true),
|
||||||
|
m_emptyCB(adoptedEmptyCB),
|
||||||
m_closeCB(adoptedCloseCB),
|
m_closeCB(adoptedCloseCB),
|
||||||
m_closed(false),
|
m_closed(false),
|
||||||
m_hungup(false)
|
m_hungup(false)
|
||||||
|
@ -39,6 +40,7 @@ CBufferedInputStream::CBufferedInputStream(
|
||||||
CBufferedInputStream::~CBufferedInputStream()
|
CBufferedInputStream::~CBufferedInputStream()
|
||||||
{
|
{
|
||||||
delete m_closeCB;
|
delete m_closeCB;
|
||||||
|
delete m_emptyCB;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -64,6 +66,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
|
||||||
if (m_closed) {
|
if (m_closed) {
|
||||||
throw XIOClosed();
|
throw XIOClosed();
|
||||||
}
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
// wait for data, hangup, or timeout
|
// wait for data, hangup, or timeout
|
||||||
CStopwatch timer(true);
|
CStopwatch timer(true);
|
||||||
|
@ -90,6 +95,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
|
||||||
if (m_buffer.getSize() == 0) {
|
if (m_buffer.getSize() == 0) {
|
||||||
m_empty = true;
|
m_empty = true;
|
||||||
m_empty.broadcast();
|
m_empty.broadcast();
|
||||||
|
if (m_emptyCB != NULL) {
|
||||||
|
m_emptyCB->run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,12 @@ public:
|
||||||
The \c mutex must not be NULL and will be used to ensure thread
|
The \c mutex must not be NULL and will be used to ensure thread
|
||||||
safe access. If \c adoptedCloseCB is not NULL it will be called
|
safe access. If \c adoptedCloseCB is not NULL it will be called
|
||||||
when close() is called, allowing the creator to detect the close.
|
when close() is called, allowing the creator to detect the close.
|
||||||
|
If adoptedEmptyCB is not NULL, it will be called whenever the
|
||||||
|
buffer becomes empty (except it won't be called by the c'tor nor
|
||||||
|
when the buffer is closed).
|
||||||
*/
|
*/
|
||||||
CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB);
|
CBufferedInputStream(CMutex* mutex,
|
||||||
|
IJob* adoptedEmptyCB, IJob* adoptedCloseCB);
|
||||||
~CBufferedInputStream();
|
~CBufferedInputStream();
|
||||||
|
|
||||||
//! @name manipulators
|
//! @name manipulators
|
||||||
|
@ -87,6 +91,7 @@ public:
|
||||||
private:
|
private:
|
||||||
CMutex* m_mutex;
|
CMutex* m_mutex;
|
||||||
CCondVar<bool> m_empty;
|
CCondVar<bool> m_empty;
|
||||||
|
IJob* m_emptyCB;
|
||||||
IJob* m_closeCB;
|
IJob* m_closeCB;
|
||||||
CStreamBuffer m_buffer;
|
CStreamBuffer m_buffer;
|
||||||
bool m_closed;
|
bool m_closed;
|
||||||
|
|
|
@ -24,8 +24,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
CBufferedOutputStream::CBufferedOutputStream(
|
CBufferedOutputStream::CBufferedOutputStream(
|
||||||
CMutex* mutex, IJob* adoptedCloseCB) :
|
CMutex* mutex, IJob* adoptedFillCB, IJob* adoptedCloseCB) :
|
||||||
m_mutex(mutex),
|
m_mutex(mutex),
|
||||||
|
m_fillCB(adoptedFillCB),
|
||||||
m_closeCB(adoptedCloseCB),
|
m_closeCB(adoptedCloseCB),
|
||||||
m_empty(mutex, true),
|
m_empty(mutex, true),
|
||||||
m_closed(false)
|
m_closed(false)
|
||||||
|
@ -36,6 +37,7 @@ CBufferedOutputStream::CBufferedOutputStream(
|
||||||
CBufferedOutputStream::~CBufferedOutputStream()
|
CBufferedOutputStream::~CBufferedOutputStream()
|
||||||
{
|
{
|
||||||
delete m_closeCB;
|
delete m_closeCB;
|
||||||
|
delete m_fillCB;
|
||||||
}
|
}
|
||||||
|
|
||||||
const void*
|
const void*
|
||||||
|
@ -68,7 +70,6 @@ CBufferedOutputStream::close()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_closed = true;
|
m_closed = true;
|
||||||
m_buffer.pop(m_buffer.getSize());
|
|
||||||
if (m_closeCB != NULL) {
|
if (m_closeCB != NULL) {
|
||||||
m_closeCB->run();
|
m_closeCB->run();
|
||||||
}
|
}
|
||||||
|
@ -82,7 +83,13 @@ CBufferedOutputStream::write(const void* buffer, UInt32 n)
|
||||||
throw XIOClosed();
|
throw XIOClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wasEmpty = (m_buffer.getSize() == 0);
|
||||||
m_buffer.write(buffer, n);
|
m_buffer.write(buffer, n);
|
||||||
|
if (wasEmpty && n > 0) {
|
||||||
|
if (m_fillCB != NULL) {
|
||||||
|
m_fillCB->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,8 +36,11 @@ public:
|
||||||
The \c mutex must not be NULL and will be used to ensure thread
|
The \c mutex must not be NULL and will be used to ensure thread
|
||||||
safe access. If \c adoptedCloseCB is not NULL it will be called
|
safe access. If \c adoptedCloseCB is not NULL it will be called
|
||||||
when close() is called, allowing the creator to detect the close.
|
when close() is called, allowing the creator to detect the close.
|
||||||
|
If \c adoptedFillCB is not NULL, it will be called whenever the
|
||||||
|
buffer becomes non-empty.
|
||||||
*/
|
*/
|
||||||
CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB);
|
CBufferedOutputStream(CMutex* mutex,
|
||||||
|
IJob* adoptedFillCB, IJob* adoptedCloseCB);
|
||||||
~CBufferedOutputStream();
|
~CBufferedOutputStream();
|
||||||
|
|
||||||
//! @name manipulators
|
//! @name manipulators
|
||||||
|
@ -79,6 +82,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CMutex* m_mutex;
|
CMutex* m_mutex;
|
||||||
|
IJob* m_fillCB;
|
||||||
IJob* m_closeCB;
|
IJob* m_closeCB;
|
||||||
CCondVar<bool> m_empty;
|
CCondVar<bool> m_empty;
|
||||||
CStreamBuffer m_buffer;
|
CStreamBuffer m_buffer;
|
||||||
|
|
|
@ -109,6 +109,12 @@ CThread::waitForEvent(double timeout) const
|
||||||
return s_map[ARCH->waitForEvent(m_thread, timeout)];
|
return s_map[ARCH->waitForEvent(m_thread, timeout)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CThread::unblock() const
|
||||||
|
{
|
||||||
|
ARCH->unblockThread(m_thread);
|
||||||
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
CThread::getResult() const
|
CThread::getResult() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -180,6 +180,15 @@ public:
|
||||||
*/
|
*/
|
||||||
EWaitResult waitForEvent(double timeout = -1.0) const;
|
EWaitResult waitForEvent(double timeout = -1.0) const;
|
||||||
|
|
||||||
|
//! Unblock thread in system call
|
||||||
|
/*!
|
||||||
|
Cause a thread that's in a blocking system call to return. This
|
||||||
|
call may return before the thread is unblocked. If the thread is
|
||||||
|
not in a blocking system call, this call has no effect. This does
|
||||||
|
not cause CMutex::lock() or CCondVar::wait() to return prematurely.
|
||||||
|
*/
|
||||||
|
void unblock() const;
|
||||||
|
|
||||||
//! Get the exit result
|
//! Get the exit result
|
||||||
/*!
|
/*!
|
||||||
Returns the exit result. This does an implicit wait(). It returns
|
Returns the exit result. This does an implicit wait(). It returns
|
||||||
|
|
|
@ -13,11 +13,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CTCPListenSocket.h"
|
#include "CTCPListenSocket.h"
|
||||||
#include "CTCPSocket.h"
|
|
||||||
#include "CNetworkAddress.h"
|
#include "CNetworkAddress.h"
|
||||||
#include "XIO.h"
|
#include "CSocketMultiplexer.h"
|
||||||
|
#include "CTCPSocket.h"
|
||||||
|
#include "TSocketMultiplexerMethodJob.h"
|
||||||
#include "XSocket.h"
|
#include "XSocket.h"
|
||||||
#include "CThread.h"
|
#include "XIO.h"
|
||||||
|
#include "CEvent.h"
|
||||||
|
#include "CEventQueue.h"
|
||||||
|
#include "CLock.h"
|
||||||
|
#include "CMutex.h"
|
||||||
#include "CArch.h"
|
#include "CArch.h"
|
||||||
#include "XArch.h"
|
#include "XArch.h"
|
||||||
|
|
||||||
|
@ -25,8 +30,10 @@
|
||||||
// CTCPListenSocket
|
// CTCPListenSocket
|
||||||
//
|
//
|
||||||
|
|
||||||
CTCPListenSocket::CTCPListenSocket()
|
CTCPListenSocket::CTCPListenSocket() :
|
||||||
|
m_target(NULL)
|
||||||
{
|
{
|
||||||
|
m_mutex = new CMutex;
|
||||||
try {
|
try {
|
||||||
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
|
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
|
||||||
}
|
}
|
||||||
|
@ -38,19 +45,28 @@ CTCPListenSocket::CTCPListenSocket()
|
||||||
CTCPListenSocket::~CTCPListenSocket()
|
CTCPListenSocket::~CTCPListenSocket()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (m_socket != NULL) {
|
||||||
|
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||||
ARCH->closeSocket(m_socket);
|
ARCH->closeSocket(m_socket);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
delete m_mutex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CTCPListenSocket::bind(const CNetworkAddress& addr)
|
CTCPListenSocket::bind(const CNetworkAddress& addr)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
CLock lock(m_mutex);
|
||||||
ARCH->bindSocket(m_socket, addr.getAddress());
|
ARCH->bindSocket(m_socket, addr.getAddress());
|
||||||
ARCH->listenOnSocket(m_socket);
|
ARCH->listenOnSocket(m_socket);
|
||||||
|
CSocketMultiplexer::getInstance()->addSocket(this,
|
||||||
|
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
|
||||||
|
this, &CTCPListenSocket::serviceListening,
|
||||||
|
m_socket, true, false));
|
||||||
}
|
}
|
||||||
catch (XArchNetworkAddressInUse& e) {
|
catch (XArchNetworkAddressInUse& e) {
|
||||||
throw XSocketAddressInUse(e.what());
|
throw XSocketAddressInUse(e.what());
|
||||||
|
@ -63,32 +79,27 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
|
||||||
IDataSocket*
|
IDataSocket*
|
||||||
CTCPListenSocket::accept()
|
CTCPListenSocket::accept()
|
||||||
{
|
{
|
||||||
// accept asynchronously so we can check for cancellation
|
|
||||||
IArchNetwork::CPollEntry pfds[1];
|
|
||||||
pfds[0].m_socket = m_socket;
|
|
||||||
pfds[0].m_events = IArchNetwork::kPOLLIN;
|
|
||||||
for (;;) {
|
|
||||||
ARCH->testCancelThread();
|
|
||||||
try {
|
try {
|
||||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
CSocketMultiplexer::getInstance()->addSocket(this,
|
||||||
if (status > 0 &&
|
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
|
||||||
(pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) {
|
this, &CTCPListenSocket::serviceListening,
|
||||||
|
m_socket, true, false));
|
||||||
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
|
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (XArchNetwork&) {
|
catch (XArchNetwork&) {
|
||||||
// ignore and retry
|
return NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CTCPListenSocket::close()
|
CTCPListenSocket::close()
|
||||||
{
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
if (m_socket == NULL) {
|
if (m_socket == NULL) {
|
||||||
throw XIOClosed();
|
throw XIOClosed();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||||
ARCH->closeSocket(m_socket);
|
ARCH->closeSocket(m_socket);
|
||||||
m_socket = NULL;
|
m_socket = NULL;
|
||||||
}
|
}
|
||||||
|
@ -96,3 +107,27 @@ CTCPListenSocket::close()
|
||||||
throw XSocketIOClose(e.what());
|
throw XSocketIOClose(e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CTCPListenSocket::setEventTarget(void* target)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
m_target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
|
||||||
|
bool read, bool, bool error)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
close();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (read) {
|
||||||
|
CEventQueue::getInstance()->addEvent(
|
||||||
|
CEvent(getConnectingEvent(), m_target, NULL));
|
||||||
|
// stop polling on this socket until the client accepts
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
#include "IListenSocket.h"
|
#include "IListenSocket.h"
|
||||||
#include "IArchNetwork.h"
|
#include "IArchNetwork.h"
|
||||||
|
|
||||||
|
class CMutex;
|
||||||
|
class ISocketMultiplexerJob;
|
||||||
|
|
||||||
//! TCP listen socket
|
//! TCP listen socket
|
||||||
/*!
|
/*!
|
||||||
A listen socket using TCP.
|
A listen socket using TCP.
|
||||||
|
@ -30,12 +33,20 @@ public:
|
||||||
// ISocket overrides
|
// ISocket overrides
|
||||||
virtual void bind(const CNetworkAddress&);
|
virtual void bind(const CNetworkAddress&);
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
virtual void setEventTarget(void*);
|
||||||
|
|
||||||
// IListenSocket overrides
|
// IListenSocket overrides
|
||||||
virtual IDataSocket* accept();
|
virtual IDataSocket* accept();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceListening(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CArchSocket m_socket;
|
CArchSocket m_socket;
|
||||||
|
CMutex* m_mutex;
|
||||||
|
void* m_target;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,14 +13,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CTCPSocket.h"
|
#include "CTCPSocket.h"
|
||||||
|
#include "CNetworkAddress.h"
|
||||||
|
#include "CSocketMultiplexer.h"
|
||||||
|
#include "TSocketMultiplexerMethodJob.h"
|
||||||
#include "CBufferedInputStream.h"
|
#include "CBufferedInputStream.h"
|
||||||
#include "CBufferedOutputStream.h"
|
#include "CBufferedOutputStream.h"
|
||||||
#include "CNetworkAddress.h"
|
|
||||||
#include "XIO.h"
|
|
||||||
#include "XSocket.h"
|
#include "XSocket.h"
|
||||||
|
#include "XIO.h"
|
||||||
#include "CLock.h"
|
#include "CLock.h"
|
||||||
#include "CMutex.h"
|
#include "CMutex.h"
|
||||||
#include "CThread.h"
|
#include "CEventQueue.h"
|
||||||
#include "TMethodJob.h"
|
#include "TMethodJob.h"
|
||||||
#include "CArch.h"
|
#include "CArch.h"
|
||||||
#include "XArch.h"
|
#include "XArch.h"
|
||||||
|
@ -45,21 +47,18 @@ CTCPSocket::CTCPSocket(CArchSocket socket) :
|
||||||
{
|
{
|
||||||
assert(m_socket != NULL);
|
assert(m_socket != NULL);
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
// socket starts in connected state
|
// socket starts in connected state
|
||||||
m_connected = kReadWrite;
|
init();
|
||||||
|
setState(kReadWrite, true);
|
||||||
// start handling socket
|
|
||||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
|
||||||
this, &CTCPSocket::ioThread));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CTCPSocket::~CTCPSocket()
|
CTCPSocket::~CTCPSocket()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
if (m_socket != NULL) {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (...) {
|
catch (...) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
@ -87,44 +86,28 @@ CTCPSocket::bind(const CNetworkAddress& addr)
|
||||||
void
|
void
|
||||||
CTCPSocket::close()
|
CTCPSocket::close()
|
||||||
{
|
{
|
||||||
// see if buffers should be flushed
|
|
||||||
bool doFlush = false;
|
|
||||||
{
|
|
||||||
CLock lock(m_mutex);
|
|
||||||
doFlush = (m_thread != NULL && (m_connected & kWrite) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// flush buffers
|
// flush buffers
|
||||||
if (doFlush) {
|
|
||||||
m_output->flush();
|
m_output->flush();
|
||||||
}
|
|
||||||
|
|
||||||
// cause ioThread to exit
|
// now closed
|
||||||
if (m_socket != NULL) {
|
setState(kClosed, true);
|
||||||
CLock lock(m_mutex);
|
|
||||||
|
// close buffers
|
||||||
try {
|
try {
|
||||||
ARCH->closeSocketForRead(m_socket);
|
m_input->close();
|
||||||
}
|
}
|
||||||
catch (XArchNetwork&) {
|
catch (...) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ARCH->closeSocketForWrite(m_socket);
|
m_output->close();
|
||||||
}
|
}
|
||||||
catch (XArchNetwork&) {
|
catch (...) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
m_connected = kClosed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for thread
|
|
||||||
if (m_thread != NULL) {
|
|
||||||
m_thread->wait();
|
|
||||||
delete m_thread;
|
|
||||||
m_thread = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// close socket
|
// close socket
|
||||||
|
CLock lock(m_mutex);
|
||||||
if (m_socket != NULL) {
|
if (m_socket != NULL) {
|
||||||
try {
|
try {
|
||||||
ARCH->closeSocket(m_socket);
|
ARCH->closeSocket(m_socket);
|
||||||
|
@ -136,65 +119,28 @@ CTCPSocket::close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CTCPSocket::setEventTarget(void* target)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
m_target = target;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CTCPSocket::connect(const CNetworkAddress& addr)
|
CTCPSocket::connect(const CNetworkAddress& addr)
|
||||||
{
|
{
|
||||||
do {
|
|
||||||
// connect asynchronously so we can check for cancellation.
|
|
||||||
// we can't wrap setting and resetting the blocking flag in
|
|
||||||
// the c'tor/d'tor of a class (to make resetting automatic)
|
|
||||||
// because setBlockingOnSocket() can throw and it might be
|
|
||||||
// called while unwinding the stack due to a throw.
|
|
||||||
try {
|
try {
|
||||||
ARCH->setBlockingOnSocket(m_socket, false);
|
// FIXME -- don't throw if in progress, just return that info
|
||||||
ARCH->connectSocket(m_socket, addr.getAddress());
|
ARCH->connectSocket(m_socket, addr.getAddress());
|
||||||
ARCH->setBlockingOnSocket(m_socket, true);
|
setState(kReadWrite, true);
|
||||||
|
|
||||||
// connected
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
catch (XArchNetworkConnecting&) {
|
catch (XArchNetworkConnecting&) {
|
||||||
// connection is in progress
|
// connection is in progress
|
||||||
ARCH->setBlockingOnSocket(m_socket, true);
|
setState(kConnecting, true);
|
||||||
}
|
|
||||||
catch (XArchNetwork& e) {
|
|
||||||
ARCH->setBlockingOnSocket(m_socket, true);
|
|
||||||
throw XSocketConnect(e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for connection or failure
|
|
||||||
IArchNetwork::CPollEntry pfds[1];
|
|
||||||
pfds[0].m_socket = m_socket;
|
|
||||||
pfds[0].m_events = IArchNetwork::kPOLLOUT;
|
|
||||||
for (;;) {
|
|
||||||
ARCH->testCancelThread();
|
|
||||||
try {
|
|
||||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
|
||||||
if (status > 0) {
|
|
||||||
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR |
|
|
||||||
IArchNetwork::kPOLLNVAL)) != 0) {
|
|
||||||
// connection failed
|
|
||||||
ARCH->throwErrorOnSocket(m_socket);
|
|
||||||
}
|
|
||||||
if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) {
|
|
||||||
// connection may have failed or succeeded
|
|
||||||
ARCH->throwErrorOnSocket(m_socket);
|
|
||||||
|
|
||||||
// connected!
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (XArchNetwork& e) {
|
catch (XArchNetwork& e) {
|
||||||
throw XSocketConnect(e.what());
|
throw XSocketConnect(e.what());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
// start servicing the socket
|
|
||||||
m_connected = kReadWrite;
|
|
||||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
|
||||||
this, &CTCPSocket::ioThread));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IInputStream*
|
IInputStream*
|
||||||
|
@ -213,14 +159,23 @@ void
|
||||||
CTCPSocket::init()
|
CTCPSocket::init()
|
||||||
{
|
{
|
||||||
m_mutex = new CMutex;
|
m_mutex = new CMutex;
|
||||||
m_thread = NULL;
|
|
||||||
m_connected = kClosed;
|
|
||||||
m_input = new CBufferedInputStream(m_mutex,
|
m_input = new CBufferedInputStream(m_mutex,
|
||||||
|
new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::emptyInput),
|
||||||
new TMethodJob<CTCPSocket>(
|
new TMethodJob<CTCPSocket>(
|
||||||
this, &CTCPSocket::closeInput));
|
this, &CTCPSocket::closeInput));
|
||||||
m_output = new CBufferedOutputStream(m_mutex,
|
m_output = new CBufferedOutputStream(m_mutex,
|
||||||
|
new TMethodJob<CTCPSocket>(
|
||||||
|
this, &CTCPSocket::fillOutput),
|
||||||
new TMethodJob<CTCPSocket>(
|
new TMethodJob<CTCPSocket>(
|
||||||
this, &CTCPSocket::closeOutput));
|
this, &CTCPSocket::closeOutput));
|
||||||
|
m_state = kUnconnected;
|
||||||
|
m_target = NULL;
|
||||||
|
m_job = NULL;
|
||||||
|
|
||||||
|
// make socket non-blocking
|
||||||
|
// FIXME -- check for error
|
||||||
|
ARCH->setBlockingOnSocket(m_socket, false);
|
||||||
|
|
||||||
// turn off Nagle algorithm. we send lots of very short messages
|
// turn off Nagle algorithm. we send lots of very short messages
|
||||||
// that should be sent without (much) delay. for example, the
|
// that should be sent without (much) delay. for example, the
|
||||||
|
@ -241,115 +196,101 @@ CTCPSocket::init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ISocketMultiplexerJob*
|
||||||
CTCPSocket::ioThread(void*)
|
CTCPSocket::newMultiplexerJob(JobFunc func, bool readable, bool writable)
|
||||||
{
|
{
|
||||||
try {
|
return new TSocketMultiplexerMethodJob<CTCPSocket>(
|
||||||
ioService();
|
this, func, m_socket, readable, writable);
|
||||||
ioCleanup();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
ioCleanup();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ISocketMultiplexerJob*
|
||||||
CTCPSocket::ioCleanup()
|
CTCPSocket::setState(State state, bool setJob)
|
||||||
{
|
{
|
||||||
try {
|
if (m_state == state || m_state == kClosed) {
|
||||||
m_input->close();
|
return m_job;
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
m_output->close();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CTCPSocket::ioService()
|
|
||||||
{
|
|
||||||
assert(m_socket != NULL);
|
|
||||||
|
|
||||||
// now service the connection
|
|
||||||
IArchNetwork::CPollEntry pfds[1];
|
|
||||||
pfds[0].m_socket = m_socket;
|
|
||||||
for (;;) {
|
|
||||||
{
|
|
||||||
// choose events to poll for
|
|
||||||
CLock lock(m_mutex);
|
|
||||||
pfds[0].m_events = 0;
|
|
||||||
if (m_connected == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((m_connected & kRead) != 0) {
|
|
||||||
// still open for reading
|
|
||||||
pfds[0].m_events |= IArchNetwork::kPOLLIN;
|
|
||||||
}
|
|
||||||
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
|
|
||||||
// data queued for writing
|
|
||||||
pfds[0].m_events |= IArchNetwork::kPOLLOUT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
State oldState = m_state;
|
||||||
// check for status
|
m_state = state;
|
||||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
|
||||||
|
|
||||||
// transfer data and handle errors
|
bool read = (m_input->getSize() > 0);
|
||||||
if (status == 1) {
|
bool write = (m_output->getSize() > 0);
|
||||||
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR |
|
CEvent::Type eventType = 0;
|
||||||
IArchNetwork::kPOLLNVAL)) != 0) {
|
m_job = NULL;
|
||||||
// stream is no good anymore so bail
|
switch (m_state) {
|
||||||
CLock lock(m_mutex);
|
case kUnconnected:
|
||||||
|
assert(0 && "cannot re-enter unconnected state");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kConnecting:
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnecting, false, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kReadWrite:
|
||||||
|
if (oldState == kConnecting) {
|
||||||
|
eventType = IDataSocket::getConnectedEvent();
|
||||||
|
}
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kReadOnly:
|
||||||
|
if (!write) {
|
||||||
|
eventType = IDataSocket::getShutdownOutputEvent();
|
||||||
|
}
|
||||||
|
if (oldState == kWriteOnly) {
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kWriteOnly:
|
||||||
|
if (!read) {
|
||||||
m_input->hangup();
|
m_input->hangup();
|
||||||
return;
|
eventType = IDataSocket::getShutdownInputEvent();
|
||||||
}
|
}
|
||||||
|
if (oldState == kReadOnly) {
|
||||||
|
goto shutdown;
|
||||||
|
}
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
|
||||||
|
break;
|
||||||
|
|
||||||
// read some data
|
case kShutdown:
|
||||||
if (pfds[0].m_revents & IArchNetwork::kPOLLIN) {
|
shutdown:
|
||||||
UInt8 buffer[4096];
|
if (!read && !write) {
|
||||||
size_t n = ARCH->readSocket(m_socket,
|
eventType = ISocket::getDisconnectedEvent();
|
||||||
buffer, sizeof(buffer));
|
m_state = kClosed;
|
||||||
CLock lock(m_mutex);
|
|
||||||
if (n > 0) {
|
|
||||||
m_input->write(buffer, n);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// stream hungup
|
m_state = kShutdown;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case kClosed:
|
||||||
m_input->hangup();
|
m_input->hangup();
|
||||||
m_connected &= ~kRead;
|
if (oldState == kConnecting) {
|
||||||
|
eventType = IDataSocket::getConnectionFailedEvent();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
eventType = ISocket::getDisconnectedEvent();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write some data
|
// notify
|
||||||
if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) {
|
if (eventType != 0) {
|
||||||
CLock lock(m_mutex);
|
sendEvent(eventType);
|
||||||
|
}
|
||||||
|
|
||||||
// get amount of data to write
|
// cut over to new job. multiplexer will delete the old job.
|
||||||
UInt32 n = m_output->getSize();
|
if (setJob) {
|
||||||
|
if (m_job == NULL) {
|
||||||
// write data
|
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||||
const void* buffer = m_output->peek(n);
|
}
|
||||||
size_t n2 = ARCH->writeSocket(m_socket, buffer, n);
|
else {
|
||||||
|
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||||
// discard written data
|
|
||||||
if (n2 > 0) {
|
|
||||||
m_output->pop(n2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (XArchNetwork&) {
|
|
||||||
// socket has failed
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return m_job;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -358,7 +299,7 @@ CTCPSocket::closeInput(void*)
|
||||||
// note -- m_mutex should already be locked
|
// note -- m_mutex should already be locked
|
||||||
try {
|
try {
|
||||||
ARCH->closeSocketForRead(m_socket);
|
ARCH->closeSocketForRead(m_socket);
|
||||||
m_connected &= ~kRead;
|
setState(kWriteOnly, true);
|
||||||
}
|
}
|
||||||
catch (XArchNetwork&) {
|
catch (XArchNetwork&) {
|
||||||
// ignore
|
// ignore
|
||||||
|
@ -370,10 +311,156 @@ CTCPSocket::closeOutput(void*)
|
||||||
{
|
{
|
||||||
// note -- m_mutex should already be locked
|
// note -- m_mutex should already be locked
|
||||||
try {
|
try {
|
||||||
ARCH->closeSocketForWrite(m_socket);
|
// ARCH->closeSocketForWrite(m_socket);
|
||||||
m_connected &= ~kWrite;
|
setState(kReadOnly, true);
|
||||||
}
|
}
|
||||||
catch (XArchNetwork&) {
|
catch (XArchNetwork&) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CTCPSocket::emptyInput(void*)
|
||||||
|
{
|
||||||
|
// note -- m_mutex should already be locked
|
||||||
|
bool write = (m_output->getSize() > 0);
|
||||||
|
if (m_state == kWriteOnly && !write) {
|
||||||
|
m_state = kShutdown;
|
||||||
|
}
|
||||||
|
if (m_state == kWriteOnly) {
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
|
||||||
|
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||||
|
m_input->hangup();
|
||||||
|
sendEvent(IDataSocket::getShutdownInputEvent());
|
||||||
|
}
|
||||||
|
else if (m_state == kShutdown) {
|
||||||
|
m_job = NULL;
|
||||||
|
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||||
|
if (!write) {
|
||||||
|
sendEvent(ISocket::getDisconnectedEvent());
|
||||||
|
m_state = kClosed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CTCPSocket::fillOutput(void*)
|
||||||
|
{
|
||||||
|
// note -- m_mutex should already be locked
|
||||||
|
if (m_state == kReadWrite) {
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, true);
|
||||||
|
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||||
|
}
|
||||||
|
else if (m_state == kWriteOnly) {
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, true);
|
||||||
|
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
|
||||||
|
bool, bool write, bool error)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
|
||||||
|
if (write && !error) {
|
||||||
|
try {
|
||||||
|
// connection may have failed or succeeded
|
||||||
|
ARCH->throwErrorOnSocket(m_socket);
|
||||||
|
}
|
||||||
|
catch (XArchNetwork&) {
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return setState(kClosed, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
return setState(kReadWrite, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
CTCPSocket::serviceConnected(ISocketMultiplexerJob* job,
|
||||||
|
bool read, bool write, bool error)
|
||||||
|
{
|
||||||
|
CLock lock(m_mutex);
|
||||||
|
if (error) {
|
||||||
|
return setState(kClosed, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write) {
|
||||||
|
// get amount of data to write
|
||||||
|
UInt32 n = m_output->getSize();
|
||||||
|
|
||||||
|
// write data
|
||||||
|
try {
|
||||||
|
const void* buffer = m_output->peek(n);
|
||||||
|
size_t n2 = ARCH->writeSocket(m_socket, buffer, n);
|
||||||
|
|
||||||
|
// discard written data
|
||||||
|
if (n2 > 0) {
|
||||||
|
m_output->pop(n2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (XArchNetworkDisconnected&) {
|
||||||
|
// stream hungup
|
||||||
|
return setState(kReadOnly, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read) {
|
||||||
|
UInt8 buffer[4096];
|
||||||
|
size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
|
||||||
|
if (n > 0) {
|
||||||
|
// slurp up as much as possible
|
||||||
|
do {
|
||||||
|
m_input->write(buffer, n);
|
||||||
|
try {
|
||||||
|
n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
|
||||||
|
}
|
||||||
|
catch (XArchNetworkWouldBlock&) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (n > 0);
|
||||||
|
|
||||||
|
// notify
|
||||||
|
sendEvent(IDataSocket::getInputEvent());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// stream hungup
|
||||||
|
return setState(kWriteOnly, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write && m_output->getSize() == 0) {
|
||||||
|
if (m_state == kReadOnly) {
|
||||||
|
ARCH->closeSocketForWrite(m_socket);
|
||||||
|
sendEvent(IDataSocket::getShutdownOutputEvent());
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
|
||||||
|
true, false);
|
||||||
|
job = m_job;
|
||||||
|
}
|
||||||
|
else if (m_state == kReadWrite || m_state == kReadOnly) {
|
||||||
|
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
|
||||||
|
true, false);
|
||||||
|
job = m_job;
|
||||||
|
}
|
||||||
|
else if (m_state == kWriteOnly) {
|
||||||
|
m_job = NULL;
|
||||||
|
job = m_job;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CTCPSocket::sendEvent(CEvent::Type type)
|
||||||
|
{
|
||||||
|
CEventQueue::getInstance()->addEvent(CEvent(type, m_target, NULL));
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define CTCPSOCKET_H
|
#define CTCPSOCKET_H
|
||||||
|
|
||||||
#include "IDataSocket.h"
|
#include "IDataSocket.h"
|
||||||
|
#include "CEvent.h"
|
||||||
#include "BasicTypes.h"
|
#include "BasicTypes.h"
|
||||||
#include "IArchNetwork.h"
|
#include "IArchNetwork.h"
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ class CMutex;
|
||||||
class CThread;
|
class CThread;
|
||||||
class CBufferedInputStream;
|
class CBufferedInputStream;
|
||||||
class CBufferedOutputStream;
|
class CBufferedOutputStream;
|
||||||
|
class ISocketMultiplexerJob;
|
||||||
|
|
||||||
//! TCP data socket
|
//! TCP data socket
|
||||||
/*!
|
/*!
|
||||||
|
@ -37,6 +39,7 @@ public:
|
||||||
// ISocket overrides
|
// ISocket overrides
|
||||||
virtual void bind(const CNetworkAddress&);
|
virtual void bind(const CNetworkAddress&);
|
||||||
virtual void close();
|
virtual void close();
|
||||||
|
virtual void setEventTarget(void*);
|
||||||
|
|
||||||
// IDataSocket overrides
|
// IDataSocket overrides
|
||||||
virtual void connect(const CNetworkAddress&);
|
virtual void connect(const CNetworkAddress&);
|
||||||
|
@ -44,23 +47,52 @@ public:
|
||||||
virtual IOutputStream* getOutputStream();
|
virtual IOutputStream* getOutputStream();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum State {
|
||||||
|
kUnconnected,
|
||||||
|
kConnecting,
|
||||||
|
kReadWrite,
|
||||||
|
kReadOnly,
|
||||||
|
kWriteOnly,
|
||||||
|
kShutdown,
|
||||||
|
kClosed
|
||||||
|
};
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void ioThread(void*);
|
|
||||||
void ioCleanup();
|
ISocketMultiplexerJob*
|
||||||
void ioService();
|
setState(State, bool setJob);
|
||||||
|
|
||||||
void closeInput(void*);
|
void closeInput(void*);
|
||||||
void closeOutput(void*);
|
void closeOutput(void*);
|
||||||
|
void emptyInput(void*);
|
||||||
|
void fillOutput(void*);
|
||||||
|
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceConnecting(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
serviceConnected(ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
|
||||||
|
typedef ISocketMultiplexerJob* (CTCPSocket::*JobFunc)(
|
||||||
|
ISocketMultiplexerJob*,
|
||||||
|
bool, bool, bool);
|
||||||
|
ISocketMultiplexerJob*
|
||||||
|
newMultiplexerJob(JobFunc,
|
||||||
|
bool readable, bool writable);
|
||||||
|
|
||||||
|
void sendEvent(CEvent::Type);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 };
|
|
||||||
|
|
||||||
CArchSocket m_socket;
|
CArchSocket m_socket;
|
||||||
CBufferedInputStream* m_input;
|
CBufferedInputStream* m_input;
|
||||||
CBufferedOutputStream* m_output;
|
CBufferedOutputStream* m_output;
|
||||||
|
|
||||||
CMutex* m_mutex;
|
CMutex* m_mutex;
|
||||||
CThread* m_thread;
|
State m_state;
|
||||||
UInt32 m_connected;
|
void* m_target;
|
||||||
|
|
||||||
|
ISocketMultiplexerJob* m_job;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IDataSocket.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// IDataSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
CEvent::Type IDataSocket::s_connectedEvent = CEvent::kUnknown;
|
||||||
|
CEvent::Type IDataSocket::s_failedEvent = CEvent::kUnknown;
|
||||||
|
CEvent::Type IDataSocket::s_inputEvent = CEvent::kUnknown;
|
||||||
|
CEvent::Type IDataSocket::s_shutdownInputEvent = CEvent::kUnknown;
|
||||||
|
CEvent::Type IDataSocket::s_shutdownOutputEvent = CEvent::kUnknown;
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IDataSocket::getConnectedEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_connectedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IDataSocket::getConnectionFailedEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_failedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IDataSocket::getInputEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_inputEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IDataSocket::getShutdownInputEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_shutdownInputEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IDataSocket::getShutdownOutputEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_shutdownOutputEvent);
|
||||||
|
}
|
|
@ -46,6 +46,7 @@ public:
|
||||||
stream will shutdown the socket for reading.
|
stream will shutdown the socket for reading.
|
||||||
*/
|
*/
|
||||||
virtual IInputStream* getInputStream() = 0;
|
virtual IInputStream* getInputStream() = 0;
|
||||||
|
|
||||||
//! Get output stream
|
//! Get output stream
|
||||||
/*!
|
/*!
|
||||||
Returns the output stream for writing to the socket. Closing this
|
Returns the output stream for writing to the socket. Closing this
|
||||||
|
@ -53,11 +54,60 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual IOutputStream* getOutputStream() = 0;
|
virtual IOutputStream* getOutputStream() = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get connected event type
|
||||||
|
/*!
|
||||||
|
Returns the socket connected event type. A socket sends this
|
||||||
|
event when a remote connection has been established.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getConnectedEvent();
|
||||||
|
|
||||||
|
//! Get connection failed event type
|
||||||
|
/*!
|
||||||
|
Returns the socket connection failed event type. A socket sends
|
||||||
|
this event when an attempt to connect to a remote port has failed.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getConnectionFailedEvent();
|
||||||
|
|
||||||
|
//! Get input event type
|
||||||
|
/*!
|
||||||
|
Returns the socket input event type. A socket sends this
|
||||||
|
event when data is available to read from the input stream.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getInputEvent();
|
||||||
|
|
||||||
|
//! Get shutdown input event type
|
||||||
|
/*!
|
||||||
|
Returns the socket shutdown input event type. A socket sends this
|
||||||
|
event when the remote side of the connection has shutdown for
|
||||||
|
writing and there is no more data to read from the socket.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getShutdownInputEvent();
|
||||||
|
|
||||||
|
//! Get shutdown input event type
|
||||||
|
/*!
|
||||||
|
Returns the socket shutdown input event type. A socket sends this
|
||||||
|
event when the remote side of the connection has shutdown for
|
||||||
|
writing and there is no more data to read from the socket.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getShutdownOutputEvent();
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
// ISocket overrides
|
// ISocket overrides
|
||||||
virtual void bind(const CNetworkAddress&) = 0;
|
virtual void bind(const CNetworkAddress&) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
virtual void setEventTarget(void*) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static CEvent::Type s_connectedEvent;
|
||||||
|
static CEvent::Type s_failedEvent;
|
||||||
|
static CEvent::Type s_inputEvent;
|
||||||
|
static CEvent::Type s_shutdownInputEvent;
|
||||||
|
static CEvent::Type s_shutdownOutputEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "IListenSocket.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// IListenSocket
|
||||||
|
//
|
||||||
|
|
||||||
|
CEvent::Type IListenSocket::s_connectingEvent = CEvent::kUnknown;
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
IListenSocket::getConnectingEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_connectingEvent);
|
||||||
|
}
|
|
@ -31,18 +31,32 @@ public:
|
||||||
|
|
||||||
//! Accept connection
|
//! Accept connection
|
||||||
/*!
|
/*!
|
||||||
Wait for and accept a connection, returning a socket representing
|
Accept a connection, returning a socket representing the full-duplex
|
||||||
the full-duplex data stream.
|
data stream. Returns NULL if no socket is waiting to be accepted.
|
||||||
|
This is only valid after a call to \c bind().
|
||||||
(cancellation point)
|
|
||||||
*/
|
*/
|
||||||
virtual IDataSocket* accept() = 0;
|
virtual IDataSocket* accept() = 0;
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get connecting event type
|
||||||
|
/*!
|
||||||
|
Returns the socket connecting event type. A socket sends this
|
||||||
|
event when a remote connection is waiting to be accepted.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getConnectingEvent();
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
// ISocket overrides
|
// ISocket overrides
|
||||||
virtual void bind(const CNetworkAddress&) = 0;
|
virtual void bind(const CNetworkAddress&) = 0;
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
virtual void setEventTarget(void*) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static CEvent::Type s_connectingEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2002 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ISocket.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// ISocket
|
||||||
|
//
|
||||||
|
|
||||||
|
CEvent::Type ISocket::s_disconnectedEvent = CEvent::kUnknown;
|
||||||
|
|
||||||
|
CEvent::Type
|
||||||
|
ISocket::getDisconnectedEvent()
|
||||||
|
{
|
||||||
|
return CEvent::registerTypeOnce(s_disconnectedEvent);
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
#define ISOCKET_H
|
#define ISOCKET_H
|
||||||
|
|
||||||
#include "IInterface.h"
|
#include "IInterface.h"
|
||||||
|
#include "CEvent.h"
|
||||||
|
|
||||||
class CNetworkAddress;
|
class CNetworkAddress;
|
||||||
|
|
||||||
|
@ -40,7 +41,28 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual void close() = 0;
|
virtual void close() = 0;
|
||||||
|
|
||||||
|
//! Set the socket's event target
|
||||||
|
/*!
|
||||||
|
Sets the target of any events sent by the socket. The default is NULL.
|
||||||
|
*/
|
||||||
|
virtual void setEventTarget(void*) = 0;
|
||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Get disconnected event type
|
||||||
|
/*!
|
||||||
|
Returns the socket disconnected event type. A socket sends this
|
||||||
|
event when the remote side of the socket has disconnected or
|
||||||
|
shutdown.
|
||||||
|
*/
|
||||||
|
static CEvent::Type getDisconnectedEvent();
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static CEvent::Type s_disconnectedEvent;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,11 +26,16 @@ MAINTAINERCLEANFILES = \
|
||||||
noinst_LIBRARIES = libnet.a
|
noinst_LIBRARIES = libnet.a
|
||||||
libnet_a_SOURCES = \
|
libnet_a_SOURCES = \
|
||||||
CNetworkAddress.cpp \
|
CNetworkAddress.cpp \
|
||||||
|
CSocketMultiplexer.cpp \
|
||||||
CTCPListenSocket.cpp \
|
CTCPListenSocket.cpp \
|
||||||
CTCPSocket.cpp \
|
CTCPSocket.cpp \
|
||||||
CTCPSocketFactory.cpp \
|
CTCPSocketFactory.cpp \
|
||||||
|
IDataSocket.cpp \
|
||||||
|
IListenSocket.cpp \
|
||||||
|
ISocket.cpp \
|
||||||
XSocket.cpp \
|
XSocket.cpp \
|
||||||
CNetworkAddress.h \
|
CNetworkAddress.h \
|
||||||
|
CSocketMultiplexer.h \
|
||||||
CTCPListenSocket.h \
|
CTCPListenSocket.h \
|
||||||
CTCPSocket.h \
|
CTCPSocket.h \
|
||||||
CTCPSocketFactory.h \
|
CTCPSocketFactory.h \
|
||||||
|
@ -38,6 +43,8 @@ libnet_a_SOURCES = \
|
||||||
IListenSocket.h \
|
IListenSocket.h \
|
||||||
ISocket.h \
|
ISocket.h \
|
||||||
ISocketFactory.h \
|
ISocketFactory.h \
|
||||||
|
ISocketMultiplexerJob.h \
|
||||||
|
TSocketMultiplexerMethodJob.h \
|
||||||
XSocket.h \
|
XSocket.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
INCLUDES = \
|
INCLUDES = \
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "CXWindowsEventQueue.h"
|
||||||
|
#include "CEvent.h"
|
||||||
|
#include "CThread.h"
|
||||||
|
#if UNIX_LIKE
|
||||||
|
# if HAVE_POLL
|
||||||
|
# include <sys/poll.h>
|
||||||
|
# else
|
||||||
|
# if HAVE_SYS_SELECT_H
|
||||||
|
# include <sys/select.h>
|
||||||
|
# endif
|
||||||
|
# if HAVE_SYS_TIME_H
|
||||||
|
# include <sys/time.h>
|
||||||
|
# endif
|
||||||
|
# if HAVE_SYS_TYPES_H
|
||||||
|
# include <sys/types.h>
|
||||||
|
# endif
|
||||||
|
# if HAVE_UNISTD_H
|
||||||
|
# include <unistd.h>
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// CEventQueueTimer
|
||||||
|
//
|
||||||
|
|
||||||
|
class CEventQueueTimer { };
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// CXWindowsEventQueue
|
||||||
|
//
|
||||||
|
|
||||||
|
CXWindowsEventQueue::CXWindowsEventQueue(Display* display) :
|
||||||
|
m_display(display)
|
||||||
|
{
|
||||||
|
m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False);
|
||||||
|
|
||||||
|
XSetWindowAttributes attr;
|
||||||
|
m_window = XCreateWindow(m_display, DefaultRootWindow(m_display),
|
||||||
|
0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent,
|
||||||
|
0, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
CXWindowsEventQueue::~CXWindowsEventQueue()
|
||||||
|
{
|
||||||
|
XDestroyWindow(m_display, m_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CXWindowsEventQueue::processSystemEvent(CEvent& event)
|
||||||
|
{
|
||||||
|
event = CEvent(CEvent::kSystem, getSystemTarget(), &m_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CXWindowsEventQueue::processClientMessage(CEvent& event)
|
||||||
|
{
|
||||||
|
assert(m_event.xany.type == ClientMessage);
|
||||||
|
|
||||||
|
// handle user events specially
|
||||||
|
if (m_event.xclient.message_type == m_userEvent) {
|
||||||
|
// get event data
|
||||||
|
CEventData data = removeEventData(m_event.xclient.data.l[1]);
|
||||||
|
|
||||||
|
// create event
|
||||||
|
event = CEvent(static_cast<size_t>(m_event.xclient.data.l[0]),
|
||||||
|
data.first, data.second);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processSystemEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CXWindowsEventQueue::waitForEvent(double dtimeout)
|
||||||
|
{
|
||||||
|
// use poll() to wait for a message from the X server or for timeout.
|
||||||
|
// this is a good deal more efficient than polling and sleeping.
|
||||||
|
#if HAVE_POLL
|
||||||
|
struct pollfd pfds[1];
|
||||||
|
pfds[0].fd = ConnectionNumber(m_display);
|
||||||
|
pfds[0].events = POLLIN;
|
||||||
|
int timeout = (dtimeout < 0.0) ? -1 :
|
||||||
|
static_cast<int>(1000.0 * dtimeout);
|
||||||
|
#else
|
||||||
|
struct timeval timeout;
|
||||||
|
struct timeval* timeoutPtr;
|
||||||
|
if (dtimeout < 0.0) {
|
||||||
|
timeoutPtr = NULL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
timeout.tv_sec = static_cast<int>(dtimeout);
|
||||||
|
timeout.tv_usec = static_cast<int>(1.0e+6 *
|
||||||
|
(dtimeout - timeout.tv_sec));
|
||||||
|
timeoutPtr = &timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize file descriptor sets
|
||||||
|
fd_set rfds;
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(ConnectionNumber(m_display), &rfds);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// wait for message from X server or for timeout. also check
|
||||||
|
// if the thread has been cancelled. poll() should return -1
|
||||||
|
// with EINTR when the thread is cancelled.
|
||||||
|
CThread::testCancel();
|
||||||
|
#if HAVE_POLL
|
||||||
|
poll(pfds, 1, timeout);
|
||||||
|
#else
|
||||||
|
select(ConnectionNumber(m_display) + 1,
|
||||||
|
SELECT_TYPE_ARG234 &rfds,
|
||||||
|
SELECT_TYPE_ARG234 NULL,
|
||||||
|
SELECT_TYPE_ARG234 NULL,
|
||||||
|
SELECT_TYPE_ARG5 timeoutPtr);
|
||||||
|
#endif
|
||||||
|
CThread::testCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CXWindowsEventQueue::doGetEvent(CEvent& event)
|
||||||
|
{
|
||||||
|
// get next event
|
||||||
|
XNextEvent(m_display, &m_event);
|
||||||
|
|
||||||
|
// process event
|
||||||
|
if (m_event.xany.type == ClientMessage) {
|
||||||
|
processClientMessage(event);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
processSystemEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CXWindowsEventQueue::doAddEvent(CEvent::Type type, UInt32 dataID)
|
||||||
|
{
|
||||||
|
// send ourself a message
|
||||||
|
XEvent xevent;
|
||||||
|
xevent.xclient.type = ClientMessage;
|
||||||
|
xevent.xclient.window = m_window;
|
||||||
|
xevent.xclient.message_type = m_userEvent;
|
||||||
|
xevent.xclient.format = 32;
|
||||||
|
xevent.xclient.data.l[0] = static_cast<long>(type);
|
||||||
|
xevent.xclient.data.l[1] = static_cast<long>(dataID);
|
||||||
|
return (XSendEvent(m_display, m_window, False, 0, &xevent) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CXWindowsEventQueue::doIsEmpty() const
|
||||||
|
{
|
||||||
|
return (XPending(m_display) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
CEventQueueTimer*
|
||||||
|
CXWindowsEventQueue::doNewTimer(double, bool) const
|
||||||
|
{
|
||||||
|
return new CEventQueueTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CXWindowsEventQueue::doDeleteTimer(CEventQueueTimer* timer) const
|
||||||
|
{
|
||||||
|
delete timer;
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* synergy -- mouse and keyboard sharing utility
|
||||||
|
* Copyright (C) 2004 Chris Schoeneman
|
||||||
|
*
|
||||||
|
* This package is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* found in the file COPYING that should have accompanied this file.
|
||||||
|
*
|
||||||
|
* This package is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CXWINDOWSEVENTQUEUE_H
|
||||||
|
#define CXWINDOWSEVENTQUEUE_H
|
||||||
|
|
||||||
|
#include "CEventQueue.h"
|
||||||
|
#if defined(X_DISPLAY_MISSING)
|
||||||
|
# error X11 is required to build synergy
|
||||||
|
#else
|
||||||
|
# include <X11/Xlib.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Event queue for X11
|
||||||
|
class CXWindowsEventQueue : public CEventQueue {
|
||||||
|
public:
|
||||||
|
CXWindowsEventQueue(Display*);
|
||||||
|
virtual ~CXWindowsEventQueue();
|
||||||
|
|
||||||
|
//! @name manipulators
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
//! @name accessors
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//@}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// CEventQueue overrides
|
||||||
|
virtual void waitForEvent(double timeout);
|
||||||
|
virtual bool doGetEvent(CEvent& event);
|
||||||
|
virtual bool doAddEvent(CEvent::Type type, UInt32 dataID);
|
||||||
|
virtual bool doIsEmpty() const;
|
||||||
|
virtual CEventQueueTimer*
|
||||||
|
doNewTimer(double duration, bool oneShot) const;
|
||||||
|
virtual void doDeleteTimer(CEventQueueTimer*) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processSystemEvent(CEvent& event);
|
||||||
|
void processClientMessage(CEvent& event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Display* m_display;
|
||||||
|
Window m_window;
|
||||||
|
Atom m_userEvent;
|
||||||
|
XEvent m_event;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -49,6 +49,7 @@ libplatform_a_SOURCES = \
|
||||||
CXWindowsClipboardTextConverter.cpp \
|
CXWindowsClipboardTextConverter.cpp \
|
||||||
CXWindowsClipboardUCS2Converter.cpp \
|
CXWindowsClipboardUCS2Converter.cpp \
|
||||||
CXWindowsClipboardUTF8Converter.cpp \
|
CXWindowsClipboardUTF8Converter.cpp \
|
||||||
|
CXWindowsEventQueue.cpp \
|
||||||
CXWindowsKeyMapper.cpp \
|
CXWindowsKeyMapper.cpp \
|
||||||
CXWindowsScreen.cpp \
|
CXWindowsScreen.cpp \
|
||||||
CXWindowsScreenSaver.cpp \
|
CXWindowsScreenSaver.cpp \
|
||||||
|
@ -57,6 +58,7 @@ libplatform_a_SOURCES = \
|
||||||
CXWindowsClipboardTextConverter.h \
|
CXWindowsClipboardTextConverter.h \
|
||||||
CXWindowsClipboardUCS2Converter.h \
|
CXWindowsClipboardUCS2Converter.h \
|
||||||
CXWindowsClipboardUTF8Converter.h \
|
CXWindowsClipboardUTF8Converter.h \
|
||||||
|
CXWindowsEventQueue.h \
|
||||||
CXWindowsKeyMapper.h \
|
CXWindowsKeyMapper.h \
|
||||||
CXWindowsScreen.h \
|
CXWindowsScreen.h \
|
||||||
CXWindowsScreenSaver.h \
|
CXWindowsScreenSaver.h \
|
||||||
|
|
Loading…
Reference in New Issue