mirror of
https://github.com/debauchee/barrier.git
synced 2024-12-24 19:42:24 +03:00
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);
|
||||
}
|
||||
|
||||
void
|
||||
CArch::unblockThread(CArchThread thread)
|
||||
{
|
||||
m_mt->unblockThread(thread);
|
||||
}
|
||||
|
||||
bool
|
||||
CArch::isSameThread(CArchThread thread1, CArchThread thread2)
|
||||
{
|
||||
|
@ -117,6 +117,7 @@ public:
|
||||
virtual void testCancelThread();
|
||||
virtual bool wait(CArchThread, double timeout);
|
||||
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
||||
virtual void unblockThread(CArchThread thread);
|
||||
virtual bool isSameThread(CArchThread, CArchThread);
|
||||
virtual bool isExitedThread(CArchThread);
|
||||
virtual void* getResultOfThread(CArchThread);
|
||||
@ -189,4 +190,20 @@ private:
|
||||
IArchTime* m_time;
|
||||
};
|
||||
|
||||
//! Convenience object to lock/unlock an arch mutex
|
||||
class CArchMutexLock {
|
||||
public:
|
||||
CArchMutexLock(CArchMutex mutex) : m_mutex(mutex)
|
||||
{
|
||||
ARCH->lockMutex(m_mutex);
|
||||
}
|
||||
~CArchMutexLock()
|
||||
{
|
||||
ARCH->unlockMutex(m_mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
CArchMutex m_mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -522,6 +522,12 @@ CArchMultithreadPosix::waitForEvent(CArchThread, double /*timeout*/)
|
||||
return kTimeout;
|
||||
}
|
||||
|
||||
void
|
||||
CArchMultithreadPosix::unblockThread(CArchThread thread)
|
||||
{
|
||||
pthread_kill(thread->m_thread, SIGWAKEUP);
|
||||
}
|
||||
|
||||
bool
|
||||
CArchMultithreadPosix::isSameThread(CArchThread thread1, CArchThread thread2)
|
||||
{
|
||||
|
@ -56,6 +56,7 @@ public:
|
||||
virtual void testCancelThread();
|
||||
virtual bool wait(CArchThread, double timeout);
|
||||
virtual EWaitResult waitForEvent(CArchThread, double timeout);
|
||||
virtual void unblockThread(CArchThread thread);
|
||||
virtual bool isSameThread(CArchThread, CArchThread);
|
||||
virtual bool isExitedThread(CArchThread);
|
||||
virtual void* getResultOfThread(CArchThread);
|
||||
|
@ -200,11 +200,6 @@ CArchNetworkBSD::acceptSocket(CArchSocket s, CArchNetAddress* addr)
|
||||
ARCH->testCancelThread();
|
||||
continue;
|
||||
}
|
||||
if (err == ECONNABORTED) {
|
||||
// connection was aborted; try again
|
||||
ARCH->testCancelThread();
|
||||
continue;
|
||||
}
|
||||
delete newSocket;
|
||||
delete *addr;
|
||||
*addr = NULL;
|
||||
@ -244,11 +239,6 @@ CArchNetworkBSD::connectSocket(CArchSocket s, CArchNetAddress addr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (errno == EAGAIN) {
|
||||
// connecting
|
||||
throw XArchNetworkConnecting(new XArchEvalUnix(errno));
|
||||
}
|
||||
|
||||
throwError(errno);
|
||||
}
|
||||
} while (false);
|
||||
@ -290,14 +280,15 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||
}
|
||||
|
||||
// do the poll
|
||||
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
|
||||
int n;
|
||||
do {
|
||||
n = poll(pfd, num, static_cast<int>(1000.0 * timeout));
|
||||
n = poll(pfd, num, t);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR) {
|
||||
// interrupted system call
|
||||
ARCH->testCancelThread();
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
throwError(errno);
|
||||
}
|
||||
@ -382,7 +373,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||
// prepare timeout for select
|
||||
struct timeval timeout2;
|
||||
struct timeval* timeout2P;
|
||||
if (timeout < 0) {
|
||||
if (timeout < 0.0) {
|
||||
timeout2P = NULL;
|
||||
}
|
||||
else {
|
||||
@ -404,7 +395,7 @@ CArchNetworkBSD::pollSocket(CPollEntry pe[], int num, double timeout)
|
||||
if (errno == EINTR) {
|
||||
// interrupted system call
|
||||
ARCH->testCancelThread();
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
throwError(errno);
|
||||
}
|
||||
|
@ -228,6 +228,15 @@ public:
|
||||
*/
|
||||
virtual EWaitResult waitForEvent(CArchThread thread, double timeout) = 0;
|
||||
|
||||
//! Unblock thread in system call
|
||||
/*!
|
||||
Cause a thread that's in a blocking system call to return. This
|
||||
call may return before the thread is unblocked. If the thread is
|
||||
not in a blocking system call, this call has no effect. This does
|
||||
not cause a lockMutex() or waitCondVar() to return prematurely.
|
||||
*/
|
||||
virtual void unblockThread(CArchThread thread) = 0;
|
||||
|
||||
//! Compare threads
|
||||
/*!
|
||||
Returns true iff two thread objects refer to the same thread.
|
||||
|
89
lib/base/CEvent.cpp
Normal file
89
lib/base/CEvent.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
100
lib/base/CEvent.h
Normal file
100
lib/base/CEvent.h
Normal file
@ -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
|
381
lib/base/CEventQueue.cpp
Normal file
381
lib/base/CEventQueue.cpp
Normal file
@ -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;
|
||||
}
|
192
lib/base/CEventQueue.h
Normal file
192
lib/base/CEventQueue.h
Normal file
@ -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
|
40
lib/base/CFunctionEventJob.cpp
Normal file
40
lib/base/CFunctionEventJob.cpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
38
lib/base/CFunctionEventJob.h
Normal file
38
lib/base/CFunctionEventJob.h
Normal file
@ -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 +
|
||||
g_prioritySuffixLength;
|
||||
|
||||
//! Convenience object to lock/unlock a mutex
|
||||
class CLogLock {
|
||||
public:
|
||||
CLogLock(CArchMutex mutex) : m_mutex(mutex) { ARCH->lockMutex(m_mutex); }
|
||||
~CLogLock() { ARCH->unlockMutex(m_mutex); }
|
||||
|
||||
private:
|
||||
CArchMutex m_mutex;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// CLog
|
||||
@ -198,7 +188,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
|
||||
assert(outputter != NULL);
|
||||
assert(outputter->getNewline() != NULL);
|
||||
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
if (alwaysAtHead) {
|
||||
m_alwaysOutputters.push_front(outputter);
|
||||
}
|
||||
@ -214,7 +204,7 @@ CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
|
||||
void
|
||||
CLog::remove(ILogOutputter* outputter)
|
||||
{
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_outputters.remove(outputter);
|
||||
m_alwaysOutputters.remove(outputter);
|
||||
}
|
||||
@ -222,7 +212,7 @@ CLog::remove(ILogOutputter* outputter)
|
||||
void
|
||||
CLog::pop_front(bool alwaysAtHead)
|
||||
{
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
|
||||
if (!list->empty()) {
|
||||
delete list->front();
|
||||
@ -248,14 +238,14 @@ CLog::setFilter(const char* maxPriority)
|
||||
void
|
||||
CLog::setFilter(int maxPriority)
|
||||
{
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_maxPriority = maxPriority;
|
||||
}
|
||||
|
||||
int
|
||||
CLog::getFilter() const
|
||||
{
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
return m_maxPriority;
|
||||
}
|
||||
|
||||
@ -276,7 +266,7 @@ CLog::output(int priority, char* msg) const
|
||||
}
|
||||
|
||||
// write to each outputter
|
||||
CLogLock lock(m_mutex);
|
||||
CArchMutexLock lock(m_mutex);
|
||||
for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
|
||||
index != m_alwaysOutputters.end();
|
||||
++index) {
|
||||
|
@ -56,6 +56,13 @@ public:
|
||||
c.pop_back();
|
||||
}
|
||||
|
||||
//! Erase element
|
||||
void erase(iterator i)
|
||||
{
|
||||
c.erase(i);
|
||||
std::make_heap(c.begin(), c.end(), comp);
|
||||
}
|
||||
|
||||
//! Get start iterator
|
||||
iterator begin()
|
||||
{
|
||||
|
88
lib/base/CSimpleEventQueue.cpp
Normal file
88
lib/base/CSimpleEventQueue.cpp
Normal file
@ -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;
|
||||
}
|
60
lib/base/CSimpleEventQueue.h
Normal file
60
lib/base/CSimpleEventQueue.h
Normal file
@ -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
|
32
lib/base/IEventJob.h
Normal file
32
lib/base/IEventJob.h
Normal file
@ -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
|
142
lib/base/IEventQueue.h
Normal file
142
lib/base/IEventQueue.h
Normal file
@ -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
|
||||
libbase_a_SOURCES = \
|
||||
CEvent.cpp \
|
||||
CEventQueue.cpp \
|
||||
CFunctionEventJob.cpp \
|
||||
CFunctionJob.cpp \
|
||||
CJobList.cpp \
|
||||
CLog.cpp \
|
||||
CSimpleEventQueue.cpp \
|
||||
CStopwatch.cpp \
|
||||
CStringUtil.cpp \
|
||||
CUnicode.cpp \
|
||||
LogOutputters.cpp \
|
||||
XBase.cpp \
|
||||
CEvent.h \
|
||||
CEventQueue.h \
|
||||
CFunctionJob.h \
|
||||
CJobList.h \
|
||||
CLog.h \
|
||||
CPriorityQueue.h \
|
||||
CSimpleEventQueue.h \
|
||||
CStopwatch.h \
|
||||
CString.h \
|
||||
CStringUtil.h \
|
||||
CUnicode.h \
|
||||
IEventQueue.h \
|
||||
IJob.h \
|
||||
ILogOutputter.h \
|
||||
LogOutputters.h \
|
||||
TMethodEventJob.h \
|
||||
TMethodJob.h \
|
||||
XBase.h \
|
||||
$(NULL)
|
||||
|
70
lib/base/TMethodEventJob.h
Normal file
70
lib/base/TMethodEventJob.h
Normal file
@ -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(
|
||||
CMutex* mutex, IJob* adoptedCloseCB) :
|
||||
CMutex* mutex, IJob* adoptedEmptyCB, IJob* adoptedCloseCB) :
|
||||
m_mutex(mutex),
|
||||
m_empty(mutex, true),
|
||||
m_emptyCB(adoptedEmptyCB),
|
||||
m_closeCB(adoptedCloseCB),
|
||||
m_closed(false),
|
||||
m_hungup(false)
|
||||
@ -39,6 +40,7 @@ CBufferedInputStream::CBufferedInputStream(
|
||||
CBufferedInputStream::~CBufferedInputStream()
|
||||
{
|
||||
delete m_closeCB;
|
||||
delete m_emptyCB;
|
||||
}
|
||||
|
||||
void
|
||||
@ -64,6 +66,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
|
||||
if (m_closed) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
if (n == 0) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// wait for data, hangup, or timeout
|
||||
CStopwatch timer(true);
|
||||
@ -90,6 +95,9 @@ CBufferedInputStream::readNoLock(void* buffer, UInt32 n, double timeout)
|
||||
if (m_buffer.getSize() == 0) {
|
||||
m_empty = true;
|
||||
m_empty.broadcast();
|
||||
if (m_emptyCB != NULL) {
|
||||
m_emptyCB->run();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
@ -36,8 +36,12 @@ public:
|
||||
The \c mutex must not be NULL and will be used to ensure thread
|
||||
safe access. If \c adoptedCloseCB is not NULL it will be called
|
||||
when close() is called, allowing the creator to detect the close.
|
||||
If adoptedEmptyCB is not NULL, it will be called whenever the
|
||||
buffer becomes empty (except it won't be called by the c'tor nor
|
||||
when the buffer is closed).
|
||||
*/
|
||||
CBufferedInputStream(CMutex* mutex, IJob* adoptedCloseCB);
|
||||
CBufferedInputStream(CMutex* mutex,
|
||||
IJob* adoptedEmptyCB, IJob* adoptedCloseCB);
|
||||
~CBufferedInputStream();
|
||||
|
||||
//! @name manipulators
|
||||
@ -87,6 +91,7 @@ public:
|
||||
private:
|
||||
CMutex* m_mutex;
|
||||
CCondVar<bool> m_empty;
|
||||
IJob* m_emptyCB;
|
||||
IJob* m_closeCB;
|
||||
CStreamBuffer m_buffer;
|
||||
bool m_closed;
|
||||
|
@ -24,8 +24,9 @@
|
||||
//
|
||||
|
||||
CBufferedOutputStream::CBufferedOutputStream(
|
||||
CMutex* mutex, IJob* adoptedCloseCB) :
|
||||
CMutex* mutex, IJob* adoptedFillCB, IJob* adoptedCloseCB) :
|
||||
m_mutex(mutex),
|
||||
m_fillCB(adoptedFillCB),
|
||||
m_closeCB(adoptedCloseCB),
|
||||
m_empty(mutex, true),
|
||||
m_closed(false)
|
||||
@ -36,6 +37,7 @@ CBufferedOutputStream::CBufferedOutputStream(
|
||||
CBufferedOutputStream::~CBufferedOutputStream()
|
||||
{
|
||||
delete m_closeCB;
|
||||
delete m_fillCB;
|
||||
}
|
||||
|
||||
const void*
|
||||
@ -68,7 +70,6 @@ CBufferedOutputStream::close()
|
||||
}
|
||||
|
||||
m_closed = true;
|
||||
m_buffer.pop(m_buffer.getSize());
|
||||
if (m_closeCB != NULL) {
|
||||
m_closeCB->run();
|
||||
}
|
||||
@ -82,7 +83,13 @@ CBufferedOutputStream::write(const void* buffer, UInt32 n)
|
||||
throw XIOClosed();
|
||||
}
|
||||
|
||||
bool wasEmpty = (m_buffer.getSize() == 0);
|
||||
m_buffer.write(buffer, n);
|
||||
if (wasEmpty && n > 0) {
|
||||
if (m_fillCB != NULL) {
|
||||
m_fillCB->run();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -36,8 +36,11 @@ public:
|
||||
The \c mutex must not be NULL and will be used to ensure thread
|
||||
safe access. If \c adoptedCloseCB is not NULL it will be called
|
||||
when close() is called, allowing the creator to detect the close.
|
||||
If \c adoptedFillCB is not NULL, it will be called whenever the
|
||||
buffer becomes non-empty.
|
||||
*/
|
||||
CBufferedOutputStream(CMutex* mutex, IJob* adoptedCloseCB);
|
||||
CBufferedOutputStream(CMutex* mutex,
|
||||
IJob* adoptedFillCB, IJob* adoptedCloseCB);
|
||||
~CBufferedOutputStream();
|
||||
|
||||
//! @name manipulators
|
||||
@ -79,6 +82,7 @@ public:
|
||||
|
||||
private:
|
||||
CMutex* m_mutex;
|
||||
IJob* m_fillCB;
|
||||
IJob* m_closeCB;
|
||||
CCondVar<bool> m_empty;
|
||||
CStreamBuffer m_buffer;
|
||||
|
@ -109,6 +109,12 @@ CThread::waitForEvent(double timeout) const
|
||||
return s_map[ARCH->waitForEvent(m_thread, timeout)];
|
||||
}
|
||||
|
||||
void
|
||||
CThread::unblock() const
|
||||
{
|
||||
ARCH->unblockThread(m_thread);
|
||||
}
|
||||
|
||||
void*
|
||||
CThread::getResult() const
|
||||
{
|
||||
|
@ -180,6 +180,15 @@ public:
|
||||
*/
|
||||
EWaitResult waitForEvent(double timeout = -1.0) const;
|
||||
|
||||
//! Unblock thread in system call
|
||||
/*!
|
||||
Cause a thread that's in a blocking system call to return. This
|
||||
call may return before the thread is unblocked. If the thread is
|
||||
not in a blocking system call, this call has no effect. This does
|
||||
not cause CMutex::lock() or CCondVar::wait() to return prematurely.
|
||||
*/
|
||||
void unblock() const;
|
||||
|
||||
//! Get the exit result
|
||||
/*!
|
||||
Returns the exit result. This does an implicit wait(). It returns
|
||||
|
@ -13,11 +13,16 @@
|
||||
*/
|
||||
|
||||
#include "CTCPListenSocket.h"
|
||||
#include "CTCPSocket.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "XIO.h"
|
||||
#include "CSocketMultiplexer.h"
|
||||
#include "CTCPSocket.h"
|
||||
#include "TSocketMultiplexerMethodJob.h"
|
||||
#include "XSocket.h"
|
||||
#include "CThread.h"
|
||||
#include "XIO.h"
|
||||
#include "CEvent.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CArch.h"
|
||||
#include "XArch.h"
|
||||
|
||||
@ -25,8 +30,10 @@
|
||||
// CTCPListenSocket
|
||||
//
|
||||
|
||||
CTCPListenSocket::CTCPListenSocket()
|
||||
CTCPListenSocket::CTCPListenSocket() :
|
||||
m_target(NULL)
|
||||
{
|
||||
m_mutex = new CMutex;
|
||||
try {
|
||||
m_socket = ARCH->newSocket(IArchNetwork::kINET, IArchNetwork::kSTREAM);
|
||||
}
|
||||
@ -38,19 +45,28 @@ CTCPListenSocket::CTCPListenSocket()
|
||||
CTCPListenSocket::~CTCPListenSocket()
|
||||
{
|
||||
try {
|
||||
ARCH->closeSocket(m_socket);
|
||||
if (m_socket != NULL) {
|
||||
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||
ARCH->closeSocket(m_socket);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
delete m_mutex;
|
||||
}
|
||||
|
||||
void
|
||||
CTCPListenSocket::bind(const CNetworkAddress& addr)
|
||||
{
|
||||
try {
|
||||
CLock lock(m_mutex);
|
||||
ARCH->bindSocket(m_socket, addr.getAddress());
|
||||
ARCH->listenOnSocket(m_socket);
|
||||
CSocketMultiplexer::getInstance()->addSocket(this,
|
||||
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
|
||||
this, &CTCPListenSocket::serviceListening,
|
||||
m_socket, true, false));
|
||||
}
|
||||
catch (XArchNetworkAddressInUse& e) {
|
||||
throw XSocketAddressInUse(e.what());
|
||||
@ -63,32 +79,27 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
|
||||
IDataSocket*
|
||||
CTCPListenSocket::accept()
|
||||
{
|
||||
// accept asynchronously so we can check for cancellation
|
||||
IArchNetwork::CPollEntry pfds[1];
|
||||
pfds[0].m_socket = m_socket;
|
||||
pfds[0].m_events = IArchNetwork::kPOLLIN;
|
||||
for (;;) {
|
||||
ARCH->testCancelThread();
|
||||
try {
|
||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
||||
if (status > 0 &&
|
||||
(pfds[0].m_revents & IArchNetwork::kPOLLIN) != 0) {
|
||||
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
|
||||
}
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore and retry
|
||||
}
|
||||
try {
|
||||
CSocketMultiplexer::getInstance()->addSocket(this,
|
||||
new TSocketMultiplexerMethodJob<CTCPListenSocket>(
|
||||
this, &CTCPListenSocket::serviceListening,
|
||||
m_socket, true, false));
|
||||
return new CTCPSocket(ARCH->acceptSocket(m_socket, NULL));
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPListenSocket::close()
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (m_socket == NULL) {
|
||||
throw XIOClosed();
|
||||
}
|
||||
try {
|
||||
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||
ARCH->closeSocket(m_socket);
|
||||
m_socket = NULL;
|
||||
}
|
||||
@ -96,3 +107,27 @@ CTCPListenSocket::close()
|
||||
throw XSocketIOClose(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPListenSocket::setEventTarget(void* target)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
m_target = target;
|
||||
}
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
CTCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
|
||||
bool read, bool, bool error)
|
||||
{
|
||||
if (error) {
|
||||
close();
|
||||
return NULL;
|
||||
}
|
||||
if (read) {
|
||||
CEventQueue::getInstance()->addEvent(
|
||||
CEvent(getConnectingEvent(), m_target, NULL));
|
||||
// stop polling on this socket until the client accepts
|
||||
return NULL;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
@ -18,6 +18,9 @@
|
||||
#include "IListenSocket.h"
|
||||
#include "IArchNetwork.h"
|
||||
|
||||
class CMutex;
|
||||
class ISocketMultiplexerJob;
|
||||
|
||||
//! TCP listen socket
|
||||
/*!
|
||||
A listen socket using TCP.
|
||||
@ -30,12 +33,20 @@ public:
|
||||
// ISocket overrides
|
||||
virtual void bind(const CNetworkAddress&);
|
||||
virtual void close();
|
||||
virtual void setEventTarget(void*);
|
||||
|
||||
// IListenSocket overrides
|
||||
virtual IDataSocket* accept();
|
||||
|
||||
private:
|
||||
ISocketMultiplexerJob*
|
||||
serviceListening(ISocketMultiplexerJob*,
|
||||
bool, bool, bool);
|
||||
|
||||
private:
|
||||
CArchSocket m_socket;
|
||||
CMutex* m_mutex;
|
||||
void* m_target;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -13,14 +13,16 @@
|
||||
*/
|
||||
|
||||
#include "CTCPSocket.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "CSocketMultiplexer.h"
|
||||
#include "TSocketMultiplexerMethodJob.h"
|
||||
#include "CBufferedInputStream.h"
|
||||
#include "CBufferedOutputStream.h"
|
||||
#include "CNetworkAddress.h"
|
||||
#include "XIO.h"
|
||||
#include "XSocket.h"
|
||||
#include "XIO.h"
|
||||
#include "CLock.h"
|
||||
#include "CMutex.h"
|
||||
#include "CThread.h"
|
||||
#include "CEventQueue.h"
|
||||
#include "TMethodJob.h"
|
||||
#include "CArch.h"
|
||||
#include "XArch.h"
|
||||
@ -45,20 +47,17 @@ CTCPSocket::CTCPSocket(CArchSocket socket) :
|
||||
{
|
||||
assert(m_socket != NULL);
|
||||
|
||||
init();
|
||||
|
||||
// socket starts in connected state
|
||||
m_connected = kReadWrite;
|
||||
|
||||
// start handling socket
|
||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::ioThread));
|
||||
init();
|
||||
setState(kReadWrite, true);
|
||||
}
|
||||
|
||||
CTCPSocket::~CTCPSocket()
|
||||
{
|
||||
try {
|
||||
close();
|
||||
if (m_socket != NULL) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
@ -87,44 +86,28 @@ CTCPSocket::bind(const CNetworkAddress& addr)
|
||||
void
|
||||
CTCPSocket::close()
|
||||
{
|
||||
// see if buffers should be flushed
|
||||
bool doFlush = false;
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
doFlush = (m_thread != NULL && (m_connected & kWrite) != 0);
|
||||
}
|
||||
|
||||
// flush buffers
|
||||
if (doFlush) {
|
||||
m_output->flush();
|
||||
}
|
||||
m_output->flush();
|
||||
|
||||
// cause ioThread to exit
|
||||
if (m_socket != NULL) {
|
||||
CLock lock(m_mutex);
|
||||
try {
|
||||
ARCH->closeSocketForRead(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
ARCH->closeSocketForWrite(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
m_connected = kClosed;
|
||||
}
|
||||
// now closed
|
||||
setState(kClosed, true);
|
||||
|
||||
// wait for thread
|
||||
if (m_thread != NULL) {
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
m_thread = NULL;
|
||||
// close buffers
|
||||
try {
|
||||
m_input->close();
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
m_output->close();
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// close socket
|
||||
CLock lock(m_mutex);
|
||||
if (m_socket != NULL) {
|
||||
try {
|
||||
ARCH->closeSocket(m_socket);
|
||||
@ -136,65 +119,28 @@ CTCPSocket::close()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::setEventTarget(void* target)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
m_target = target;
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::connect(const CNetworkAddress& addr)
|
||||
{
|
||||
do {
|
||||
// connect asynchronously so we can check for cancellation.
|
||||
// we can't wrap setting and resetting the blocking flag in
|
||||
// the c'tor/d'tor of a class (to make resetting automatic)
|
||||
// because setBlockingOnSocket() can throw and it might be
|
||||
// called while unwinding the stack due to a throw.
|
||||
try {
|
||||
ARCH->setBlockingOnSocket(m_socket, false);
|
||||
ARCH->connectSocket(m_socket, addr.getAddress());
|
||||
ARCH->setBlockingOnSocket(m_socket, true);
|
||||
|
||||
// connected
|
||||
break;
|
||||
}
|
||||
catch (XArchNetworkConnecting&) {
|
||||
// connection is in progress
|
||||
ARCH->setBlockingOnSocket(m_socket, true);
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
ARCH->setBlockingOnSocket(m_socket, true);
|
||||
throw XSocketConnect(e.what());
|
||||
}
|
||||
|
||||
// wait for connection or failure
|
||||
IArchNetwork::CPollEntry pfds[1];
|
||||
pfds[0].m_socket = m_socket;
|
||||
pfds[0].m_events = IArchNetwork::kPOLLOUT;
|
||||
for (;;) {
|
||||
ARCH->testCancelThread();
|
||||
try {
|
||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
||||
if (status > 0) {
|
||||
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR |
|
||||
IArchNetwork::kPOLLNVAL)) != 0) {
|
||||
// connection failed
|
||||
ARCH->throwErrorOnSocket(m_socket);
|
||||
}
|
||||
if ((pfds[0].m_revents & IArchNetwork::kPOLLOUT) != 0) {
|
||||
// connection may have failed or succeeded
|
||||
ARCH->throwErrorOnSocket(m_socket);
|
||||
|
||||
// connected!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
throw XSocketConnect(e.what());
|
||||
}
|
||||
}
|
||||
} while (false);
|
||||
|
||||
// start servicing the socket
|
||||
m_connected = kReadWrite;
|
||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::ioThread));
|
||||
try {
|
||||
// FIXME -- don't throw if in progress, just return that info
|
||||
ARCH->connectSocket(m_socket, addr.getAddress());
|
||||
setState(kReadWrite, true);
|
||||
}
|
||||
catch (XArchNetworkConnecting&) {
|
||||
// connection is in progress
|
||||
setState(kConnecting, true);
|
||||
}
|
||||
catch (XArchNetwork& e) {
|
||||
throw XSocketConnect(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
IInputStream*
|
||||
@ -212,15 +158,24 @@ CTCPSocket::getOutputStream()
|
||||
void
|
||||
CTCPSocket::init()
|
||||
{
|
||||
m_mutex = new CMutex;
|
||||
m_thread = NULL;
|
||||
m_connected = kClosed;
|
||||
m_input = new CBufferedInputStream(m_mutex,
|
||||
m_mutex = new CMutex;
|
||||
m_input = new CBufferedInputStream(m_mutex,
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::emptyInput),
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::closeInput));
|
||||
m_output = new CBufferedOutputStream(m_mutex,
|
||||
m_output = new CBufferedOutputStream(m_mutex,
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::fillOutput),
|
||||
new TMethodJob<CTCPSocket>(
|
||||
this, &CTCPSocket::closeOutput));
|
||||
m_state = kUnconnected;
|
||||
m_target = NULL;
|
||||
m_job = NULL;
|
||||
|
||||
// make socket non-blocking
|
||||
// FIXME -- check for error
|
||||
ARCH->setBlockingOnSocket(m_socket, false);
|
||||
|
||||
// turn off Nagle algorithm. we send lots of very short messages
|
||||
// that should be sent without (much) delay. for example, the
|
||||
@ -241,115 +196,101 @@ CTCPSocket::init()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::ioThread(void*)
|
||||
ISocketMultiplexerJob*
|
||||
CTCPSocket::newMultiplexerJob(JobFunc func, bool readable, bool writable)
|
||||
{
|
||||
try {
|
||||
ioService();
|
||||
ioCleanup();
|
||||
}
|
||||
catch (...) {
|
||||
ioCleanup();
|
||||
throw;
|
||||
}
|
||||
return new TSocketMultiplexerMethodJob<CTCPSocket>(
|
||||
this, func, m_socket, readable, writable);
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::ioCleanup()
|
||||
ISocketMultiplexerJob*
|
||||
CTCPSocket::setState(State state, bool setJob)
|
||||
{
|
||||
try {
|
||||
m_input->close();
|
||||
if (m_state == state || m_state == kClosed) {
|
||||
return m_job;
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
try {
|
||||
m_output->close();
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::ioService()
|
||||
{
|
||||
assert(m_socket != NULL);
|
||||
State oldState = m_state;
|
||||
m_state = state;
|
||||
|
||||
// now service the connection
|
||||
IArchNetwork::CPollEntry pfds[1];
|
||||
pfds[0].m_socket = m_socket;
|
||||
for (;;) {
|
||||
{
|
||||
// choose events to poll for
|
||||
CLock lock(m_mutex);
|
||||
pfds[0].m_events = 0;
|
||||
if (m_connected == 0) {
|
||||
return;
|
||||
}
|
||||
if ((m_connected & kRead) != 0) {
|
||||
// still open for reading
|
||||
pfds[0].m_events |= IArchNetwork::kPOLLIN;
|
||||
}
|
||||
if ((m_connected & kWrite) != 0 && m_output->getSize() > 0) {
|
||||
// data queued for writing
|
||||
pfds[0].m_events |= IArchNetwork::kPOLLOUT;
|
||||
}
|
||||
bool read = (m_input->getSize() > 0);
|
||||
bool write = (m_output->getSize() > 0);
|
||||
CEvent::Type eventType = 0;
|
||||
m_job = NULL;
|
||||
switch (m_state) {
|
||||
case kUnconnected:
|
||||
assert(0 && "cannot re-enter unconnected state");
|
||||
break;
|
||||
|
||||
case kConnecting:
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnecting, false, true);
|
||||
break;
|
||||
|
||||
case kReadWrite:
|
||||
if (oldState == kConnecting) {
|
||||
eventType = IDataSocket::getConnectedEvent();
|
||||
}
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
|
||||
break;
|
||||
|
||||
try {
|
||||
// check for status
|
||||
const int status = ARCH->pollSocket(pfds, 1, 0.01);
|
||||
|
||||
// transfer data and handle errors
|
||||
if (status == 1) {
|
||||
if ((pfds[0].m_revents & (IArchNetwork::kPOLLERR |
|
||||
IArchNetwork::kPOLLNVAL)) != 0) {
|
||||
// stream is no good anymore so bail
|
||||
CLock lock(m_mutex);
|
||||
m_input->hangup();
|
||||
return;
|
||||
}
|
||||
|
||||
// read some data
|
||||
if (pfds[0].m_revents & IArchNetwork::kPOLLIN) {
|
||||
UInt8 buffer[4096];
|
||||
size_t n = ARCH->readSocket(m_socket,
|
||||
buffer, sizeof(buffer));
|
||||
CLock lock(m_mutex);
|
||||
if (n > 0) {
|
||||
m_input->write(buffer, n);
|
||||
}
|
||||
else {
|
||||
// stream hungup
|
||||
m_input->hangup();
|
||||
m_connected &= ~kRead;
|
||||
}
|
||||
}
|
||||
|
||||
// write some data
|
||||
if (pfds[0].m_revents & IArchNetwork::kPOLLOUT) {
|
||||
CLock lock(m_mutex);
|
||||
|
||||
// get amount of data to write
|
||||
UInt32 n = m_output->getSize();
|
||||
|
||||
// write data
|
||||
const void* buffer = m_output->peek(n);
|
||||
size_t n2 = ARCH->writeSocket(m_socket, buffer, n);
|
||||
|
||||
// discard written data
|
||||
if (n2 > 0) {
|
||||
m_output->pop(n2);
|
||||
}
|
||||
}
|
||||
}
|
||||
case kReadOnly:
|
||||
if (!write) {
|
||||
eventType = IDataSocket::getShutdownOutputEvent();
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// socket has failed
|
||||
return;
|
||||
if (oldState == kWriteOnly) {
|
||||
goto shutdown;
|
||||
}
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, write);
|
||||
break;
|
||||
|
||||
case kWriteOnly:
|
||||
if (!read) {
|
||||
m_input->hangup();
|
||||
eventType = IDataSocket::getShutdownInputEvent();
|
||||
}
|
||||
if (oldState == kReadOnly) {
|
||||
goto shutdown;
|
||||
}
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
|
||||
break;
|
||||
|
||||
case kShutdown:
|
||||
shutdown:
|
||||
if (!read && !write) {
|
||||
eventType = ISocket::getDisconnectedEvent();
|
||||
m_state = kClosed;
|
||||
}
|
||||
else {
|
||||
m_state = kShutdown;
|
||||
}
|
||||
break;
|
||||
|
||||
case kClosed:
|
||||
m_input->hangup();
|
||||
if (oldState == kConnecting) {
|
||||
eventType = IDataSocket::getConnectionFailedEvent();
|
||||
}
|
||||
else {
|
||||
eventType = ISocket::getDisconnectedEvent();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// notify
|
||||
if (eventType != 0) {
|
||||
sendEvent(eventType);
|
||||
}
|
||||
|
||||
// cut over to new job. multiplexer will delete the old job.
|
||||
if (setJob) {
|
||||
if (m_job == NULL) {
|
||||
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||
}
|
||||
else {
|
||||
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||
}
|
||||
}
|
||||
return m_job;
|
||||
}
|
||||
|
||||
void
|
||||
@ -358,7 +299,7 @@ CTCPSocket::closeInput(void*)
|
||||
// note -- m_mutex should already be locked
|
||||
try {
|
||||
ARCH->closeSocketForRead(m_socket);
|
||||
m_connected &= ~kRead;
|
||||
setState(kWriteOnly, true);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
@ -370,10 +311,156 @@ CTCPSocket::closeOutput(void*)
|
||||
{
|
||||
// note -- m_mutex should already be locked
|
||||
try {
|
||||
ARCH->closeSocketForWrite(m_socket);
|
||||
m_connected &= ~kWrite;
|
||||
// ARCH->closeSocketForWrite(m_socket);
|
||||
setState(kReadOnly, true);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::emptyInput(void*)
|
||||
{
|
||||
// note -- m_mutex should already be locked
|
||||
bool write = (m_output->getSize() > 0);
|
||||
if (m_state == kWriteOnly && !write) {
|
||||
m_state = kShutdown;
|
||||
}
|
||||
if (m_state == kWriteOnly) {
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, write);
|
||||
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||
m_input->hangup();
|
||||
sendEvent(IDataSocket::getShutdownInputEvent());
|
||||
}
|
||||
else if (m_state == kShutdown) {
|
||||
m_job = NULL;
|
||||
CSocketMultiplexer::getInstance()->removeSocket(this);
|
||||
if (!write) {
|
||||
sendEvent(ISocket::getDisconnectedEvent());
|
||||
m_state = kClosed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::fillOutput(void*)
|
||||
{
|
||||
// note -- m_mutex should already be locked
|
||||
if (m_state == kReadWrite) {
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, true, true);
|
||||
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||
}
|
||||
else if (m_state == kWriteOnly) {
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected, false, true);
|
||||
CSocketMultiplexer::getInstance()->addSocket(this, m_job);
|
||||
}
|
||||
}
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
CTCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
|
||||
bool, bool write, bool error)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
|
||||
if (write && !error) {
|
||||
try {
|
||||
// connection may have failed or succeeded
|
||||
ARCH->throwErrorOnSocket(m_socket);
|
||||
}
|
||||
catch (XArchNetwork&) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return setState(kClosed, false);
|
||||
}
|
||||
|
||||
if (write) {
|
||||
return setState(kReadWrite, false);
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
CTCPSocket::serviceConnected(ISocketMultiplexerJob* job,
|
||||
bool read, bool write, bool error)
|
||||
{
|
||||
CLock lock(m_mutex);
|
||||
if (error) {
|
||||
return setState(kClosed, false);
|
||||
}
|
||||
|
||||
if (write) {
|
||||
// get amount of data to write
|
||||
UInt32 n = m_output->getSize();
|
||||
|
||||
// write data
|
||||
try {
|
||||
const void* buffer = m_output->peek(n);
|
||||
size_t n2 = ARCH->writeSocket(m_socket, buffer, n);
|
||||
|
||||
// discard written data
|
||||
if (n2 > 0) {
|
||||
m_output->pop(n2);
|
||||
}
|
||||
}
|
||||
catch (XArchNetworkDisconnected&) {
|
||||
// stream hungup
|
||||
return setState(kReadOnly, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (read) {
|
||||
UInt8 buffer[4096];
|
||||
size_t n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
|
||||
if (n > 0) {
|
||||
// slurp up as much as possible
|
||||
do {
|
||||
m_input->write(buffer, n);
|
||||
try {
|
||||
n = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
|
||||
}
|
||||
catch (XArchNetworkWouldBlock&) {
|
||||
break;
|
||||
}
|
||||
} while (n > 0);
|
||||
|
||||
// notify
|
||||
sendEvent(IDataSocket::getInputEvent());
|
||||
}
|
||||
else {
|
||||
// stream hungup
|
||||
return setState(kWriteOnly, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (write && m_output->getSize() == 0) {
|
||||
if (m_state == kReadOnly) {
|
||||
ARCH->closeSocketForWrite(m_socket);
|
||||
sendEvent(IDataSocket::getShutdownOutputEvent());
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
|
||||
true, false);
|
||||
job = m_job;
|
||||
}
|
||||
else if (m_state == kReadWrite || m_state == kReadOnly) {
|
||||
m_job = newMultiplexerJob(&CTCPSocket::serviceConnected,
|
||||
true, false);
|
||||
job = m_job;
|
||||
}
|
||||
else if (m_state == kWriteOnly) {
|
||||
m_job = NULL;
|
||||
job = m_job;
|
||||
}
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
|
||||
void
|
||||
CTCPSocket::sendEvent(CEvent::Type type)
|
||||
{
|
||||
CEventQueue::getInstance()->addEvent(CEvent(type, m_target, NULL));
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define CTCPSOCKET_H
|
||||
|
||||
#include "IDataSocket.h"
|
||||
#include "CEvent.h"
|
||||
#include "BasicTypes.h"
|
||||
#include "IArchNetwork.h"
|
||||
|
||||
@ -23,6 +24,7 @@ class CMutex;
|
||||
class CThread;
|
||||
class CBufferedInputStream;
|
||||
class CBufferedOutputStream;
|
||||
class ISocketMultiplexerJob;
|
||||
|
||||
//! TCP data socket
|
||||
/*!
|
||||
@ -37,6 +39,7 @@ public:
|
||||
// ISocket overrides
|
||||
virtual void bind(const CNetworkAddress&);
|
||||
virtual void close();
|
||||
virtual void setEventTarget(void*);
|
||||
|
||||
// IDataSocket overrides
|
||||
virtual void connect(const CNetworkAddress&);
|
||||
@ -44,23 +47,52 @@ public:
|
||||
virtual IOutputStream* getOutputStream();
|
||||
|
||||
private:
|
||||
enum State {
|
||||
kUnconnected,
|
||||
kConnecting,
|
||||
kReadWrite,
|
||||
kReadOnly,
|
||||
kWriteOnly,
|
||||
kShutdown,
|
||||
kClosed
|
||||
};
|
||||
|
||||
void init();
|
||||
void ioThread(void*);
|
||||
void ioCleanup();
|
||||
void ioService();
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
setState(State, bool setJob);
|
||||
|
||||
void closeInput(void*);
|
||||
void closeOutput(void*);
|
||||
void emptyInput(void*);
|
||||
void fillOutput(void*);
|
||||
|
||||
ISocketMultiplexerJob*
|
||||
serviceConnecting(ISocketMultiplexerJob*,
|
||||
bool, bool, bool);
|
||||
ISocketMultiplexerJob*
|
||||
serviceConnected(ISocketMultiplexerJob*,
|
||||
bool, bool, bool);
|
||||
|
||||
typedef ISocketMultiplexerJob* (CTCPSocket::*JobFunc)(
|
||||
ISocketMultiplexerJob*,
|
||||
bool, bool, bool);
|
||||
ISocketMultiplexerJob*
|
||||
newMultiplexerJob(JobFunc,
|
||||
bool readable, bool writable);
|
||||
|
||||
void sendEvent(CEvent::Type);
|
||||
|
||||
private:
|
||||
enum { kClosed = 0, kRead = 1, kWrite = 2, kReadWrite = 3 };
|
||||
|
||||
CArchSocket m_socket;
|
||||
CBufferedInputStream* m_input;
|
||||
CBufferedOutputStream* m_output;
|
||||
|
||||
CMutex* m_mutex;
|
||||
CThread* m_thread;
|
||||
UInt32 m_connected;
|
||||
State m_state;
|
||||
void* m_target;
|
||||
|
||||
ISocketMultiplexerJob* m_job;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
55
lib/net/IDataSocket.cpp
Normal file
55
lib/net/IDataSocket.cpp
Normal file
@ -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.
|
||||
*/
|
||||
virtual IInputStream* getInputStream() = 0;
|
||||
|
||||
//! Get output stream
|
||||
/*!
|
||||
Returns the output stream for writing to the socket. Closing this
|
||||
@ -53,11 +54,60 @@ public:
|
||||
*/
|
||||
virtual IOutputStream* getOutputStream() = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get connected event type
|
||||
/*!
|
||||
Returns the socket connected event type. A socket sends this
|
||||
event when a remote connection has been established.
|
||||
*/
|
||||
static CEvent::Type getConnectedEvent();
|
||||
|
||||
//! Get connection failed event type
|
||||
/*!
|
||||
Returns the socket connection failed event type. A socket sends
|
||||
this event when an attempt to connect to a remote port has failed.
|
||||
*/
|
||||
static CEvent::Type getConnectionFailedEvent();
|
||||
|
||||
//! Get input event type
|
||||
/*!
|
||||
Returns the socket input event type. A socket sends this
|
||||
event when data is available to read from the input stream.
|
||||
*/
|
||||
static CEvent::Type getInputEvent();
|
||||
|
||||
//! Get shutdown input event type
|
||||
/*!
|
||||
Returns the socket shutdown input event type. A socket sends this
|
||||
event when the remote side of the connection has shutdown for
|
||||
writing and there is no more data to read from the socket.
|
||||
*/
|
||||
static CEvent::Type getShutdownInputEvent();
|
||||
|
||||
//! Get shutdown input event type
|
||||
/*!
|
||||
Returns the socket shutdown input event type. A socket sends this
|
||||
event when the remote side of the connection has shutdown for
|
||||
writing and there is no more data to read from the socket.
|
||||
*/
|
||||
static CEvent::Type getShutdownOutputEvent();
|
||||
|
||||
//@}
|
||||
|
||||
// ISocket overrides
|
||||
virtual void bind(const CNetworkAddress&) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual void setEventTarget(void*) = 0;
|
||||
|
||||
private:
|
||||
static CEvent::Type s_connectedEvent;
|
||||
static CEvent::Type s_failedEvent;
|
||||
static CEvent::Type s_inputEvent;
|
||||
static CEvent::Type s_shutdownInputEvent;
|
||||
static CEvent::Type s_shutdownOutputEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
27
lib/net/IListenSocket.cpp
Normal file
27
lib/net/IListenSocket.cpp
Normal file
@ -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
|
||||
/*!
|
||||
Wait for and accept a connection, returning a socket representing
|
||||
the full-duplex data stream.
|
||||
|
||||
(cancellation point)
|
||||
Accept a connection, returning a socket representing the full-duplex
|
||||
data stream. Returns NULL if no socket is waiting to be accepted.
|
||||
This is only valid after a call to \c bind().
|
||||
*/
|
||||
virtual IDataSocket* accept() = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get connecting event type
|
||||
/*!
|
||||
Returns the socket connecting event type. A socket sends this
|
||||
event when a remote connection is waiting to be accepted.
|
||||
*/
|
||||
static CEvent::Type getConnectingEvent();
|
||||
|
||||
//@}
|
||||
|
||||
// ISocket overrides
|
||||
virtual void bind(const CNetworkAddress&) = 0;
|
||||
virtual void close() = 0;
|
||||
virtual void setEventTarget(void*) = 0;
|
||||
|
||||
private:
|
||||
static CEvent::Type s_connectingEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
27
lib/net/ISocket.cpp
Normal file
27
lib/net/ISocket.cpp
Normal file
@ -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
|
||||
|
||||
#include "IInterface.h"
|
||||
#include "CEvent.h"
|
||||
|
||||
class CNetworkAddress;
|
||||
|
||||
@ -40,7 +41,28 @@ public:
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
//! Set the socket's event target
|
||||
/*!
|
||||
Sets the target of any events sent by the socket. The default is NULL.
|
||||
*/
|
||||
virtual void setEventTarget(void*) = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get disconnected event type
|
||||
/*!
|
||||
Returns the socket disconnected event type. A socket sends this
|
||||
event when the remote side of the socket has disconnected or
|
||||
shutdown.
|
||||
*/
|
||||
static CEvent::Type getDisconnectedEvent();
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
static CEvent::Type s_disconnectedEvent;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -15,35 +15,42 @@ NULL =
|
||||
DEPTH = ../..
|
||||
VDEPTH = ./$(VPATH)/$(DEPTH)
|
||||
|
||||
EXTRA_DIST = \
|
||||
net.dsp \
|
||||
EXTRA_DIST = \
|
||||
net.dsp \
|
||||
$(NULL)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
Makefile.in \
|
||||
MAINTAINERCLEANFILES = \
|
||||
Makefile.in \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = libnet.a
|
||||
libnet_a_SOURCES = \
|
||||
CNetworkAddress.cpp \
|
||||
CTCPListenSocket.cpp \
|
||||
CTCPSocket.cpp \
|
||||
CTCPSocketFactory.cpp \
|
||||
XSocket.cpp \
|
||||
CNetworkAddress.h \
|
||||
CTCPListenSocket.h \
|
||||
CTCPSocket.h \
|
||||
CTCPSocketFactory.h \
|
||||
IDataSocket.h \
|
||||
IListenSocket.h \
|
||||
ISocket.h \
|
||||
ISocketFactory.h \
|
||||
XSocket.h \
|
||||
libnet_a_SOURCES = \
|
||||
CNetworkAddress.cpp \
|
||||
CSocketMultiplexer.cpp \
|
||||
CTCPListenSocket.cpp \
|
||||
CTCPSocket.cpp \
|
||||
CTCPSocketFactory.cpp \
|
||||
IDataSocket.cpp \
|
||||
IListenSocket.cpp \
|
||||
ISocket.cpp \
|
||||
XSocket.cpp \
|
||||
CNetworkAddress.h \
|
||||
CSocketMultiplexer.h \
|
||||
CTCPListenSocket.h \
|
||||
CTCPSocket.h \
|
||||
CTCPSocketFactory.h \
|
||||
IDataSocket.h \
|
||||
IListenSocket.h \
|
||||
ISocket.h \
|
||||
ISocketFactory.h \
|
||||
ISocketMultiplexerJob.h \
|
||||
TSocketMultiplexerMethodJob.h \
|
||||
XSocket.h \
|
||||
$(NULL)
|
||||
INCLUDES = \
|
||||
-I$(VDEPTH)/lib/common \
|
||||
-I$(VDEPTH)/lib/arch \
|
||||
-I$(VDEPTH)/lib/base \
|
||||
-I$(VDEPTH)/lib/mt \
|
||||
-I$(VDEPTH)/lib/io \
|
||||
INCLUDES = \
|
||||
-I$(VDEPTH)/lib/common \
|
||||
-I$(VDEPTH)/lib/arch \
|
||||
-I$(VDEPTH)/lib/base \
|
||||
-I$(VDEPTH)/lib/mt \
|
||||
-I$(VDEPTH)/lib/io \
|
||||
$(NULL)
|
||||
|
182
lib/platform/CXWindowsEventQueue.cpp
Normal file
182
lib/platform/CXWindowsEventQueue.cpp
Normal file
@ -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;
|
||||
}
|
61
lib/platform/CXWindowsEventQueue.h
Normal file
61
lib/platform/CXWindowsEventQueue.h
Normal file
@ -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 \
|
||||
CXWindowsClipboardUCS2Converter.cpp \
|
||||
CXWindowsClipboardUTF8Converter.cpp \
|
||||
CXWindowsEventQueue.cpp \
|
||||
CXWindowsKeyMapper.cpp \
|
||||
CXWindowsScreen.cpp \
|
||||
CXWindowsScreenSaver.cpp \
|
||||
@ -57,6 +58,7 @@ libplatform_a_SOURCES = \
|
||||
CXWindowsClipboardTextConverter.h \
|
||||
CXWindowsClipboardUCS2Converter.h \
|
||||
CXWindowsClipboardUTF8Converter.h \
|
||||
CXWindowsEventQueue.h \
|
||||
CXWindowsKeyMapper.h \
|
||||
CXWindowsScreen.h \
|
||||
CXWindowsScreenSaver.h \
|
||||
|
Loading…
Reference in New Issue
Block a user