mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-11 00:58:14 +08:00
Initial commit of the synergy trunk sources from sf.net
This commit is contained in:
98
lib/base/CEvent.cpp
Normal file
98
lib/base/CEvent.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "CEventQueue.h"
|
||||
|
||||
//
|
||||
// CEvent
|
||||
//
|
||||
|
||||
CEvent::CEvent() :
|
||||
m_type(kUnknown),
|
||||
m_target(NULL),
|
||||
m_data(NULL),
|
||||
m_flags(0)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CEvent::CEvent(Type type, void* target, void* data, Flags flags) :
|
||||
m_type(type),
|
||||
m_target(target),
|
||||
m_data(data),
|
||||
m_flags(flags)
|
||||
{
|
||||
// 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::Flags
|
||||
CEvent::getFlags() const
|
||||
{
|
||||
return m_flags;
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CEvent::registerType(const char* name)
|
||||
{
|
||||
return EVENTQUEUE->registerType(name);
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CEvent::registerTypeOnce(Type& type, const char* name)
|
||||
{
|
||||
return EVENTQUEUE->registerTypeOnce(type, name);
|
||||
}
|
||||
|
||||
const char*
|
||||
CEvent::getTypeName(Type type)
|
||||
{
|
||||
return EVENTQUEUE->getTypeName(type);
|
||||
}
|
||||
|
||||
void
|
||||
CEvent::deleteData(const CEvent& event)
|
||||
{
|
||||
switch (event.getType()) {
|
||||
case kUnknown:
|
||||
case kQuit:
|
||||
case kSystem:
|
||||
case kTimer:
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((event.getFlags() & kDontFreeData) == 0) {
|
||||
free(event.getData());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
123
lib/base/CEvent.h
Normal file
123
lib/base/CEvent.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "stdmap.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
|
||||
};
|
||||
|
||||
typedef UInt32 Flags;
|
||||
enum {
|
||||
kNone = 0x00, //!< No flags
|
||||
kDeliverImmediately = 0x01, //!< Dispatch and free event immediately
|
||||
kDontFreeData = 0x02 //!< Don't free data in deleteData
|
||||
};
|
||||
|
||||
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) allocated by malloc(),
|
||||
which means it cannot have a constructor, destructor or be
|
||||
composed of any types that do. \p target is the intended
|
||||
recipient of the event. \p flags is any combination of \c Flags.
|
||||
*/
|
||||
CEvent(Type type, void* target = NULL, void* data = NULL,
|
||||
UInt32 flags = kNone);
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Creates a new event type
|
||||
/*!
|
||||
Returns a unique event type id.
|
||||
*/
|
||||
static Type registerType(const char* name);
|
||||
|
||||
//! 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, const char* name);
|
||||
|
||||
//! Get name for event
|
||||
/*!
|
||||
Returns the name for the event \p type. This is primarily for
|
||||
debugging.
|
||||
*/
|
||||
static const char* getTypeName(Type type);
|
||||
|
||||
//! Release event data
|
||||
/*!
|
||||
Deletes event data for the given event (using free()).
|
||||
*/
|
||||
static void deleteData(const CEvent&);
|
||||
|
||||
//@}
|
||||
//! @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;
|
||||
|
||||
//! Get event flags
|
||||
/*!
|
||||
Returns the event flags.
|
||||
*/
|
||||
Flags getFlags() const;
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
void* m_target;
|
||||
void* m_data;
|
||||
Flags m_flags;
|
||||
};
|
||||
|
||||
#endif
|
||||
526
lib/base/CEventQueue.cpp
Normal file
526
lib/base/CEventQueue.cpp
Normal file
@@ -0,0 +1,526 @@
|
||||
;/*
|
||||
* 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 "CLog.h"
|
||||
#include "CSimpleEventQueueBuffer.h"
|
||||
#include "CStopwatch.h"
|
||||
#include "IEventJob.h"
|
||||
#include "CArch.h"
|
||||
|
||||
// interrupt handler. this just adds a quit event to the queue.
|
||||
static
|
||||
void
|
||||
interrupt(CArch::ESignal, void*)
|
||||
{
|
||||
EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CEventQueue
|
||||
//
|
||||
|
||||
CEventQueue::CEventQueue() :
|
||||
m_nextType(CEvent::kLast)
|
||||
{
|
||||
setInstance(this);
|
||||
m_mutex = ARCH->newMutex();
|
||||
ARCH->setSignalHandler(CArch::kINTERRUPT, &interrupt, NULL);
|
||||
ARCH->setSignalHandler(CArch::kTERMINATE, &interrupt, NULL);
|
||||
m_buffer = new CSimpleEventQueueBuffer;
|
||||
}
|
||||
|
||||
CEventQueue::~CEventQueue()
|
||||
{
|
||||
delete m_buffer;
|
||||
ARCH->setSignalHandler(CArch::kINTERRUPT, NULL, NULL);
|
||||
ARCH->setSignalHandler(CArch::kTERMINATE, NULL, NULL);
|
||||
ARCH->closeMutex(m_mutex);
|
||||
setInstance(NULL);
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CEventQueue::registerType(const char* name)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_typeMap.insert(std::make_pair(m_nextType, name));
|
||||
LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
|
||||
return m_nextType++;
|
||||
}
|
||||
|
||||
CEvent::Type
|
||||
CEventQueue::registerTypeOnce(CEvent::Type& type, const char* name)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
if (type == CEvent::kUnknown) {
|
||||
m_typeMap.insert(std::make_pair(m_nextType, name));
|
||||
LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
|
||||
type = m_nextType++;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
const char*
|
||||
CEventQueue::getTypeName(CEvent::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case CEvent::kUnknown:
|
||||
return "nil";
|
||||
|
||||
case CEvent::kQuit:
|
||||
return "quit";
|
||||
|
||||
case CEvent::kSystem:
|
||||
return "system";
|
||||
|
||||
case CEvent::kTimer:
|
||||
return "timer";
|
||||
|
||||
default:
|
||||
CTypeMap::const_iterator i = m_typeMap.find(type);
|
||||
if (i == m_typeMap.end()) {
|
||||
return "<unknown>";
|
||||
}
|
||||
else {
|
||||
return i->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CEventQueue::adoptBuffer(IEventQueueBuffer* buffer)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
|
||||
// discard old buffer and old events
|
||||
delete m_buffer;
|
||||
for (CEventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
|
||||
CEvent::deleteData(i->second);
|
||||
}
|
||||
m_events.clear();
|
||||
m_oldEventIDs.clear();
|
||||
|
||||
// use new buffer
|
||||
m_buffer = buffer;
|
||||
if (m_buffer == NULL) {
|
||||
m_buffer = new CSimpleEventQueueBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CEventQueue::getEvent(CEvent& event, double timeout)
|
||||
{
|
||||
CStopwatch timer(true);
|
||||
retry:
|
||||
// if no events are waiting then handle timers and then wait
|
||||
while (m_buffer->isEmpty()) {
|
||||
// handle timers first
|
||||
if (hasTimerExpired(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// get time remaining in timeout
|
||||
double timeLeft = timeout - timer.getTime();
|
||||
if (timeout >= 0.0 && timeLeft <= 0.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) {
|
||||
timeLeft = timerTimeout;
|
||||
}
|
||||
|
||||
// wait for an event
|
||||
m_buffer->waitForEvent(timeLeft);
|
||||
}
|
||||
|
||||
// get the event
|
||||
UInt32 dataID;
|
||||
IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID);
|
||||
switch (type) {
|
||||
case IEventQueueBuffer::kNone:
|
||||
if (timeout < 0.0 || timeout <= timer.getTime()) {
|
||||
// don't want to fail if client isn't expecting that
|
||||
// so if getEvent() fails with an infinite timeout
|
||||
// then just try getting another event.
|
||||
goto retry;
|
||||
}
|
||||
return false;
|
||||
|
||||
case IEventQueueBuffer::kSystem:
|
||||
return true;
|
||||
|
||||
case IEventQueueBuffer::kUser:
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
event = removeEvent(dataID);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(0 && "invalid event type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CEventQueue::dispatchEvent(const CEvent& event)
|
||||
{
|
||||
void* target = event.getTarget();
|
||||
IEventJob* job = getHandler(event.getType(), target);
|
||||
if (job == NULL) {
|
||||
job = getHandler(CEvent::kUnknown, 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;
|
||||
}
|
||||
|
||||
if ((event.getFlags() & CEvent::kDeliverImmediately) != 0) {
|
||||
dispatchEvent(event);
|
||||
CEvent::deleteData(event);
|
||||
}
|
||||
else {
|
||||
CArchMutexLock lock(m_mutex);
|
||||
|
||||
// store the event's data locally
|
||||
UInt32 eventID = saveEvent(event);
|
||||
|
||||
// add it
|
||||
if (!m_buffer->addEvent(eventID)) {
|
||||
// failed to send event
|
||||
removeEvent(eventID);
|
||||
CEvent::deleteData(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CEventQueueTimer*
|
||||
CEventQueue::newTimer(double duration, void* target)
|
||||
{
|
||||
assert(duration > 0.0);
|
||||
|
||||
CEventQueueTimer* timer = m_buffer->newTimer(duration, false);
|
||||
if (target == NULL) {
|
||||
target = timer;
|
||||
}
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_timers.insert(timer);
|
||||
// initial duration is requested duration plus whatever's on
|
||||
// the clock currently because the latter will be subtracted
|
||||
// the next time we check for timers.
|
||||
m_timerQueue.push(CTimer(timer, duration,
|
||||
duration + m_time.getTime(), target, false));
|
||||
return timer;
|
||||
}
|
||||
|
||||
CEventQueueTimer*
|
||||
CEventQueue::newOneShotTimer(double duration, void* target)
|
||||
{
|
||||
assert(duration > 0.0);
|
||||
|
||||
CEventQueueTimer* timer = m_buffer->newTimer(duration, true);
|
||||
if (target == NULL) {
|
||||
target = timer;
|
||||
}
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_timers.insert(timer);
|
||||
// initial duration is requested duration plus whatever's on
|
||||
// the clock currently because the latter will be subtracted
|
||||
// the next time we check for timers.
|
||||
m_timerQueue.push(CTimer(timer, duration,
|
||||
duration + m_time.getTime(), 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);
|
||||
}
|
||||
m_buffer->deleteTimer(timer);
|
||||
}
|
||||
|
||||
void
|
||||
CEventQueue::adoptHandler(CEvent::Type type, void* target, IEventJob* handler)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
IEventJob*& job = m_handlers[target][type];
|
||||
delete job;
|
||||
job = handler;
|
||||
}
|
||||
|
||||
void
|
||||
CEventQueue::removeHandler(CEvent::Type type, void* target)
|
||||
{
|
||||
IEventJob* handler = NULL;
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
CHandlerTable::iterator index = m_handlers.find(target);
|
||||
if (index != m_handlers.end()) {
|
||||
CTypeHandlerTable& typeHandlers = index->second;
|
||||
CTypeHandlerTable::iterator index2 = typeHandlers.find(type);
|
||||
if (index2 != typeHandlers.end()) {
|
||||
handler = index2->second;
|
||||
typeHandlers.erase(index2);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete handler;
|
||||
}
|
||||
|
||||
void
|
||||
CEventQueue::removeHandlers(void* target)
|
||||
{
|
||||
std::vector<IEventJob*> handlers;
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
CHandlerTable::iterator index = m_handlers.find(target);
|
||||
if (index != m_handlers.end()) {
|
||||
// copy to handlers array and clear table for target
|
||||
CTypeHandlerTable& typeHandlers = index->second;
|
||||
for (CTypeHandlerTable::iterator index2 = typeHandlers.begin();
|
||||
index2 != typeHandlers.end(); ++index2) {
|
||||
handlers.push_back(index2->second);
|
||||
}
|
||||
typeHandlers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// delete handlers
|
||||
for (std::vector<IEventJob*>::iterator index = handlers.begin();
|
||||
index != handlers.end(); ++index) {
|
||||
delete *index;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CEventQueue::isEmpty() const
|
||||
{
|
||||
return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
|
||||
}
|
||||
|
||||
IEventJob*
|
||||
CEventQueue::getHandler(CEvent::Type type, void* target) const
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
CHandlerTable::const_iterator index = m_handlers.find(target);
|
||||
if (index != m_handlers.end()) {
|
||||
const CTypeHandlerTable& typeHandlers = index->second;
|
||||
CTypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
|
||||
if (index2 != typeHandlers.end()) {
|
||||
return index2->second;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UInt32
|
||||
CEventQueue::saveEvent(const CEvent& event)
|
||||
{
|
||||
// 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_events.size());
|
||||
}
|
||||
|
||||
// save data
|
||||
m_events[id] = event;
|
||||
return id;
|
||||
}
|
||||
|
||||
CEvent
|
||||
CEventQueue::removeEvent(UInt32 eventID)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CEventQueue::CTimer
|
||||
//
|
||||
|
||||
CEventQueue::CTimer::CTimer(CEventQueueTimer* timer, double timeout,
|
||||
double initialTime, void* target, bool oneShot) :
|
||||
m_timer(timer),
|
||||
m_timeout(timeout),
|
||||
m_target(target),
|
||||
m_oneShot(oneShot),
|
||||
m_time(initialTime)
|
||||
{
|
||||
assert(m_timeout > 0.0);
|
||||
}
|
||||
|
||||
CEventQueue::CTimer::~CTimer()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CEventQueue::CTimer::reset()
|
||||
{
|
||||
m_time = m_timeout;
|
||||
}
|
||||
|
||||
CEventQueue::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;
|
||||
}
|
||||
123
lib/base/CEventQueue.h
Normal file
123
lib/base/CEventQueue.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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();
|
||||
|
||||
// IEventQueue overrides
|
||||
virtual void adoptBuffer(IEventQueueBuffer*);
|
||||
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);
|
||||
virtual CEventQueueTimer*
|
||||
newOneShotTimer(double duration, void* target);
|
||||
virtual void deleteTimer(CEventQueueTimer*);
|
||||
virtual void adoptHandler(CEvent::Type type,
|
||||
void* target, IEventJob* handler);
|
||||
virtual void removeHandler(CEvent::Type type, void* target);
|
||||
virtual void removeHandlers(void* target);
|
||||
virtual CEvent::Type
|
||||
registerType(const char* name);
|
||||
virtual CEvent::Type
|
||||
registerTypeOnce(CEvent::Type& type, const char* name);
|
||||
virtual bool isEmpty() const;
|
||||
virtual IEventJob* getHandler(CEvent::Type type, void* target) const;
|
||||
virtual const char* getTypeName(CEvent::Type type);
|
||||
|
||||
private:
|
||||
UInt32 saveEvent(const CEvent& event);
|
||||
CEvent removeEvent(UInt32 eventID);
|
||||
bool hasTimerExpired(CEvent& event);
|
||||
double getNextTimerTimeout() const;
|
||||
|
||||
private:
|
||||
class CTimer {
|
||||
public:
|
||||
CTimer(CEventQueueTimer*, double timeout, double initialTime,
|
||||
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<CEvent::Type, const char*> CTypeMap;
|
||||
typedef std::map<CEvent::Type, IEventJob*> CTypeHandlerTable;
|
||||
typedef std::map<void*, CTypeHandlerTable> CHandlerTable;
|
||||
|
||||
CArchMutex m_mutex;
|
||||
|
||||
// registered events
|
||||
CEvent::Type m_nextType;
|
||||
CTypeMap m_typeMap;
|
||||
|
||||
// buffer of events
|
||||
IEventQueueBuffer* m_buffer;
|
||||
|
||||
// saved events
|
||||
CEventTable m_events;
|
||||
CEventIDList m_oldEventIDs;
|
||||
|
||||
// timers
|
||||
CStopwatch m_time;
|
||||
CTimers m_timers;
|
||||
CTimerQueue m_timerQueue;
|
||||
CTimerEvent m_timerEvent;
|
||||
|
||||
// event handlers
|
||||
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"
|
||||
|
||||
//
|
||||
// CFunctionEventJob
|
||||
//
|
||||
|
||||
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
|
||||
39
lib/base/CFunctionJob.cpp
Normal file
39
lib/base/CFunctionJob.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 "CFunctionJob.h"
|
||||
|
||||
//
|
||||
// CFunctionJob
|
||||
//
|
||||
|
||||
CFunctionJob::CFunctionJob(void (*func)(void*), void* arg) :
|
||||
m_func(func),
|
||||
m_arg(arg)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CFunctionJob::~CFunctionJob()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CFunctionJob::run()
|
||||
{
|
||||
if (m_func != NULL) {
|
||||
m_func(m_arg);
|
||||
}
|
||||
}
|
||||
38
lib/base/CFunctionJob.h
Normal file
38
lib/base/CFunctionJob.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CFUNCTIONJOB_H
|
||||
#define CFUNCTIONJOB_H
|
||||
|
||||
#include "IJob.h"
|
||||
|
||||
//! Use a function as a job
|
||||
/*!
|
||||
A job class that invokes a function.
|
||||
*/
|
||||
class CFunctionJob : public IJob {
|
||||
public:
|
||||
//! run() invokes \c func(arg)
|
||||
CFunctionJob(void (*func)(void*), void* arg = NULL);
|
||||
virtual ~CFunctionJob();
|
||||
|
||||
// IJob overrides
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
void (*m_func)(void*);
|
||||
void* m_arg;
|
||||
};
|
||||
|
||||
#endif
|
||||
293
lib/base/CLog.cpp
Normal file
293
lib/base/CLog.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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 "CLog.h"
|
||||
#include "CString.h"
|
||||
#include "CStringUtil.h"
|
||||
#include "LogOutputters.h"
|
||||
#include "CArch.h"
|
||||
#include "Version.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// names of priorities
|
||||
static const char* g_priority[] = {
|
||||
"FATAL",
|
||||
"ERROR",
|
||||
"WARNING",
|
||||
"NOTE",
|
||||
"INFO",
|
||||
"DEBUG",
|
||||
"DEBUG1",
|
||||
"DEBUG2"
|
||||
};
|
||||
|
||||
// number of priorities
|
||||
static const int g_numPriority = (int)(sizeof(g_priority) /
|
||||
sizeof(g_priority[0]));
|
||||
|
||||
// the default priority
|
||||
#if defined(NDEBUG)
|
||||
static const int g_defaultMaxPriority = 4;
|
||||
#else
|
||||
static const int g_defaultMaxPriority = 5;
|
||||
#endif
|
||||
|
||||
// length of longest string in g_priority
|
||||
static const int g_maxPriorityLength = 7;
|
||||
|
||||
// length of suffix string (": ")
|
||||
static const int g_prioritySuffixLength = 2;
|
||||
|
||||
// amount of padded required to fill in the priority prefix
|
||||
static const int g_priorityPad = g_maxPriorityLength +
|
||||
g_prioritySuffixLength;
|
||||
|
||||
|
||||
//
|
||||
// CLog
|
||||
//
|
||||
|
||||
CLog* CLog::s_log = NULL;
|
||||
|
||||
CLog::CLog()
|
||||
{
|
||||
assert(s_log == NULL);
|
||||
|
||||
// create mutex for multithread safe operation
|
||||
m_mutex = ARCH->newMutex();
|
||||
|
||||
// other initalization
|
||||
m_maxPriority = g_defaultMaxPriority;
|
||||
m_maxNewlineLength = 0;
|
||||
insert(new CConsoleLogOutputter);
|
||||
}
|
||||
|
||||
CLog::~CLog()
|
||||
{
|
||||
// clean up
|
||||
for (COutputterList::iterator index = m_outputters.begin();
|
||||
index != m_outputters.end(); ++index) {
|
||||
delete *index;
|
||||
}
|
||||
for (COutputterList::iterator index = m_alwaysOutputters.begin();
|
||||
index != m_alwaysOutputters.end(); ++index) {
|
||||
delete *index;
|
||||
}
|
||||
ARCH->closeMutex(m_mutex);
|
||||
s_log = NULL;
|
||||
}
|
||||
|
||||
CLog*
|
||||
CLog::getInstance()
|
||||
{
|
||||
// note -- not thread safe; client must initialize log safely
|
||||
if (s_log == NULL) {
|
||||
s_log = new CLog;
|
||||
}
|
||||
return s_log;
|
||||
}
|
||||
|
||||
void
|
||||
CLog::print(const char* file, int line, const char* fmt, ...) const
|
||||
{
|
||||
// check if fmt begins with a priority argument
|
||||
int priority = 4;
|
||||
if (fmt[0] == '%' && fmt[1] == 'z') {
|
||||
priority = fmt[2] - '\060';
|
||||
fmt += 3;
|
||||
}
|
||||
|
||||
// done if below priority threshold
|
||||
if (priority > getFilter()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// compute prefix padding length
|
||||
char stack[1024];
|
||||
int pPad = g_priorityPad;
|
||||
if (file != NULL) {
|
||||
sprintf(stack, "%d", line);
|
||||
pPad += strlen(file) + 1 /* comma */ +
|
||||
strlen(stack) + 1 /* colon */ + 1 /* space */;
|
||||
}
|
||||
|
||||
// compute suffix padding length
|
||||
int sPad = m_maxNewlineLength;
|
||||
|
||||
// print to buffer, leaving space for a newline at the end and prefix
|
||||
// at the beginning.
|
||||
char* buffer = stack;
|
||||
int len = (int)(sizeof(stack) / sizeof(stack[0]));
|
||||
while (true) {
|
||||
// try printing into the buffer
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int n = ARCH->vsnprintf(buffer + pPad, len - pPad - sPad, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// if the buffer wasn't big enough then make it bigger and try again
|
||||
if (n < 0 || n > (int)len) {
|
||||
if (buffer != stack) {
|
||||
delete[] buffer;
|
||||
}
|
||||
len *= 2;
|
||||
buffer = new char[len];
|
||||
}
|
||||
|
||||
// if the buffer was big enough then continue
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// print the prefix to the buffer. leave space for priority label.
|
||||
char* message = buffer;
|
||||
if (file != NULL) {
|
||||
sprintf(buffer + g_priorityPad, "%s,%d:", file, line);
|
||||
buffer[pPad - 1] = ' ';
|
||||
|
||||
// discard file and line if priority < 0
|
||||
if (priority < 0) {
|
||||
message += pPad - g_priorityPad;
|
||||
}
|
||||
}
|
||||
|
||||
// output buffer
|
||||
output(priority, message);
|
||||
|
||||
// clean up
|
||||
if (buffer != stack) {
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CLog::insert(ILogOutputter* outputter, bool alwaysAtHead)
|
||||
{
|
||||
assert(outputter != NULL);
|
||||
assert(outputter->getNewline() != NULL);
|
||||
|
||||
CArchMutexLock lock(m_mutex);
|
||||
if (alwaysAtHead) {
|
||||
m_alwaysOutputters.push_front(outputter);
|
||||
}
|
||||
else {
|
||||
m_outputters.push_front(outputter);
|
||||
}
|
||||
int newlineLength = strlen(outputter->getNewline());
|
||||
if (newlineLength > m_maxNewlineLength) {
|
||||
m_maxNewlineLength = newlineLength;
|
||||
}
|
||||
outputter->open(kAppVersion);
|
||||
outputter->show(false);
|
||||
}
|
||||
|
||||
void
|
||||
CLog::remove(ILogOutputter* outputter)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_outputters.remove(outputter);
|
||||
m_alwaysOutputters.remove(outputter);
|
||||
}
|
||||
|
||||
void
|
||||
CLog::pop_front(bool alwaysAtHead)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
|
||||
if (!list->empty()) {
|
||||
delete list->front();
|
||||
list->pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CLog::setFilter(const char* maxPriority)
|
||||
{
|
||||
if (maxPriority != NULL) {
|
||||
for (int i = 0; i < g_numPriority; ++i) {
|
||||
if (strcmp(maxPriority, g_priority[i]) == 0) {
|
||||
setFilter(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CLog::setFilter(int maxPriority)
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
m_maxPriority = maxPriority;
|
||||
}
|
||||
|
||||
int
|
||||
CLog::getFilter() const
|
||||
{
|
||||
CArchMutexLock lock(m_mutex);
|
||||
return m_maxPriority;
|
||||
}
|
||||
|
||||
void
|
||||
CLog::output(int priority, char* msg) const
|
||||
{
|
||||
assert(priority >= -1 && priority < g_numPriority);
|
||||
assert(msg != NULL);
|
||||
|
||||
// insert priority label
|
||||
int n = -g_prioritySuffixLength;
|
||||
if (priority >= 0) {
|
||||
n = strlen(g_priority[priority]);
|
||||
strcpy(msg + g_maxPriorityLength - n, g_priority[priority]);
|
||||
msg[g_maxPriorityLength + 0] = ':';
|
||||
msg[g_maxPriorityLength + 1] = ' ';
|
||||
msg[g_maxPriorityLength + 1] = ' ';
|
||||
}
|
||||
|
||||
// find end of message
|
||||
char* end = msg + g_priorityPad + strlen(msg + g_priorityPad);
|
||||
|
||||
// write to each outputter
|
||||
CArchMutexLock lock(m_mutex);
|
||||
for (COutputterList::const_iterator index = m_alwaysOutputters.begin();
|
||||
index != m_alwaysOutputters.end();
|
||||
++index) {
|
||||
// get outputter
|
||||
ILogOutputter* outputter = *index;
|
||||
|
||||
// put an appropriate newline at the end
|
||||
strcpy(end, outputter->getNewline());
|
||||
|
||||
// write message
|
||||
outputter->write(static_cast<ILogOutputter::ELevel>(priority),
|
||||
msg + g_maxPriorityLength - n);
|
||||
}
|
||||
for (COutputterList::const_iterator index = m_outputters.begin();
|
||||
index != m_outputters.end(); ++index) {
|
||||
// get outputter
|
||||
ILogOutputter* outputter = *index;
|
||||
|
||||
// put an appropriate newline at the end
|
||||
strcpy(end, outputter->getNewline());
|
||||
|
||||
// write message and break out of loop if it returns false
|
||||
if (!outputter->write(static_cast<ILogOutputter::ELevel>(priority),
|
||||
msg + g_maxPriorityLength - n)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
202
lib/base/CLog.h
Normal file
202
lib/base/CLog.h
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CLOG_H
|
||||
#define CLOG_H
|
||||
|
||||
#include "common.h"
|
||||
#include "IArchMultithread.h"
|
||||
#include "stdlist.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#define CLOG (CLog::getInstance())
|
||||
|
||||
class ILogOutputter;
|
||||
|
||||
//! Logging facility
|
||||
/*!
|
||||
The logging class; all console output should go through this class.
|
||||
It supports multithread safe operation, several message priority levels,
|
||||
filtering by priority, and output redirection. The macros LOG() and
|
||||
LOGC() provide convenient access.
|
||||
*/
|
||||
class CLog {
|
||||
public:
|
||||
//! Log levels
|
||||
/*!
|
||||
The logging priority levels in order of highest to lowest priority.
|
||||
*/
|
||||
enum ELevel {
|
||||
kFATAL, //!< For fatal errors
|
||||
kERROR, //!< For serious errors
|
||||
kWARNING, //!< For minor errors and warnings
|
||||
kNOTE, //!< For messages about notable events
|
||||
kINFO, //!< For informational messages
|
||||
kDEBUG, //!< For important debugging messages
|
||||
kDEBUG1, //!< For more detailed debugging messages
|
||||
kDEBUG2 //!< For even more detailed debugging messages
|
||||
};
|
||||
|
||||
~CLog();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Add an outputter to the head of the list
|
||||
/*!
|
||||
Inserts an outputter to the head of the outputter list. When the
|
||||
logger writes a message, it goes to the outputter at the head of
|
||||
the outputter list. If that outputter's \c write() method returns
|
||||
true then it also goes to the next outputter, as so on until an
|
||||
outputter returns false or there are no more outputters. Outputters
|
||||
still in the outputter list when the log is destroyed will be
|
||||
deleted. If \c alwaysAtHead is true then the outputter is always
|
||||
called before all outputters with \c alwaysAtHead false and the
|
||||
return value of the outputter is ignored.
|
||||
|
||||
By default, the logger has one outputter installed which writes to
|
||||
the console.
|
||||
*/
|
||||
void insert(ILogOutputter* adopted,
|
||||
bool alwaysAtHead = false);
|
||||
|
||||
//! Remove an outputter from the list
|
||||
/*!
|
||||
Removes the first occurrence of the given outputter from the
|
||||
outputter list. It does nothing if the outputter is not in the
|
||||
list. The outputter is not deleted.
|
||||
*/
|
||||
void remove(ILogOutputter* orphaned);
|
||||
|
||||
//! Remove the outputter from the head of the list
|
||||
/*!
|
||||
Removes and deletes the outputter at the head of the outputter list.
|
||||
This does nothing if the outputter list is empty. Only removes
|
||||
outputters that were inserted with the matching \c alwaysAtHead.
|
||||
*/
|
||||
void pop_front(bool alwaysAtHead = false);
|
||||
|
||||
//! Set the minimum priority filter.
|
||||
/*!
|
||||
Set the filter. Messages below this priority are discarded.
|
||||
The default priority is 4 (INFO) (unless built without NDEBUG
|
||||
in which case it's 5 (DEBUG)). setFilter(const char*) returns
|
||||
true if the priority \c name was recognized; if \c name is NULL
|
||||
then it simply returns true.
|
||||
*/
|
||||
bool setFilter(const char* name);
|
||||
void setFilter(int);
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Print a log message
|
||||
/*!
|
||||
Print a log message using the printf-like \c format and arguments
|
||||
preceded by the filename and line number. If \c file is NULL then
|
||||
neither the file nor the line are printed.
|
||||
*/
|
||||
void print(const char* file, int line,
|
||||
const char* format, ...) const;
|
||||
|
||||
//! Get the minimum priority level.
|
||||
int getFilter() const;
|
||||
|
||||
//! Get the singleton instance of the log
|
||||
static CLog* getInstance();
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
CLog();
|
||||
|
||||
void output(int priority, char* msg) const;
|
||||
|
||||
private:
|
||||
typedef std::list<ILogOutputter*> COutputterList;
|
||||
|
||||
static CLog* s_log;
|
||||
|
||||
CArchMutex m_mutex;
|
||||
COutputterList m_outputters;
|
||||
COutputterList m_alwaysOutputters;
|
||||
int m_maxNewlineLength;
|
||||
int m_maxPriority;
|
||||
};
|
||||
|
||||
/*!
|
||||
\def LOG(arg)
|
||||
Write to the log. Because macros cannot accept variable arguments, this
|
||||
should be invoked like so:
|
||||
\code
|
||||
LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal"));
|
||||
\endcode
|
||||
In particular, notice the double open and close parentheses. Also note
|
||||
that there is no comma after the \c CLOG_XXX. The \c XXX should be
|
||||
replaced by one of enumerants in \c CLog::ELevel without the leading
|
||||
\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will
|
||||
not be filtered and is never prefixed by the filename and line number.
|
||||
|
||||
If \c NOLOGGING is defined during the build then this macro expands to
|
||||
nothing. If \c NDEBUG is defined during the build then it expands to a
|
||||
call to CLog::print. Otherwise it expands to a call to CLog::printt,
|
||||
which includes the filename and line number.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\def LOGC(expr, arg)
|
||||
Write to the log if and only if expr is true. Because macros cannot accept
|
||||
variable arguments, this should be invoked like so:
|
||||
\code
|
||||
LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y));
|
||||
\endcode
|
||||
In particular, notice the parentheses around everything after the boolean
|
||||
expression. Also note that there is no comma after the \c CLOG_XXX.
|
||||
The \c XXX should be replaced by one of enumerants in \c CLog::ELevel
|
||||
without the leading \c k. For example, \c CLOG_INFO. The special
|
||||
\c CLOG_PRINT level will not be filtered and is never prefixed by the
|
||||
filename and line number.
|
||||
|
||||
If \c NOLOGGING is defined during the build then this macro expands to
|
||||
nothing. If \c NDEBUG is not defined during the build then it expands
|
||||
to a call to CLog::print that prints the filename and line number,
|
||||
otherwise it expands to a call that doesn't.
|
||||
*/
|
||||
|
||||
#if defined(NOLOGGING)
|
||||
#define LOG(_a1)
|
||||
#define LOGC(_a1, _a2)
|
||||
#define CLOG_TRACE
|
||||
#elif defined(NDEBUG)
|
||||
#define LOG(_a1) CLOG->print _a1
|
||||
#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
|
||||
#define CLOG_TRACE NULL, 0,
|
||||
#else
|
||||
#define LOG(_a1) CLOG->print _a1
|
||||
#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
|
||||
#define CLOG_TRACE __FILE__, __LINE__,
|
||||
#endif
|
||||
|
||||
#define CLOG_PRINT CLOG_TRACE "%z\057"
|
||||
#define CLOG_CRIT CLOG_TRACE "%z\060"
|
||||
#define CLOG_ERR CLOG_TRACE "%z\061"
|
||||
#define CLOG_WARN CLOG_TRACE "%z\062"
|
||||
#define CLOG_NOTE CLOG_TRACE "%z\063"
|
||||
#define CLOG_INFO CLOG_TRACE "%z\064"
|
||||
#define CLOG_DEBUG CLOG_TRACE "%z\065"
|
||||
#define CLOG_DEBUG1 CLOG_TRACE "%z\066"
|
||||
#define CLOG_DEBUG2 CLOG_TRACE "%z\067"
|
||||
|
||||
#endif
|
||||
136
lib/base/CPriorityQueue.h
Normal file
136
lib/base/CPriorityQueue.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2003 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 CPRIORITYQUEUE_H
|
||||
#define CPRIORITYQUEUE_H
|
||||
|
||||
#include "stdvector.h"
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
//! A priority queue with an iterator
|
||||
/*!
|
||||
This priority queue is the same as a standard priority queue except:
|
||||
it sorts by std::greater, it has a forward iterator through the elements
|
||||
(which can appear in any order), and its contents can be swapped.
|
||||
*/
|
||||
template <class T, class Container = std::vector<T>,
|
||||
#if defined(_MSC_VER)
|
||||
class Compare = std::greater<Container::value_type> >
|
||||
#else
|
||||
class Compare = std::greater<typename Container::value_type> >
|
||||
#endif
|
||||
class CPriorityQueue {
|
||||
public:
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::iterator iterator;
|
||||
typedef typename Container::const_iterator const_iterator;
|
||||
typedef Container container_type;
|
||||
|
||||
CPriorityQueue() { }
|
||||
CPriorityQueue(Container& swappedIn) { swap(swappedIn); }
|
||||
~CPriorityQueue() { }
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Add element
|
||||
void push(const value_type& v)
|
||||
{
|
||||
c.push_back(v);
|
||||
std::push_heap(c.begin(), c.end(), comp);
|
||||
}
|
||||
|
||||
//! Remove head element
|
||||
void pop()
|
||||
{
|
||||
std::pop_heap(c.begin(), c.end(), comp);
|
||||
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()
|
||||
{
|
||||
return c.begin();
|
||||
}
|
||||
|
||||
//! Get end iterator
|
||||
iterator end()
|
||||
{
|
||||
return c.end();
|
||||
}
|
||||
|
||||
//! Swap contents with another priority queue
|
||||
void swap(CPriorityQueue<T, Container, Compare>& q)
|
||||
{
|
||||
c.swap(q.c);
|
||||
}
|
||||
|
||||
//! Swap contents with another container
|
||||
void swap(Container& c2)
|
||||
{
|
||||
c.swap(c2);
|
||||
std::make_heap(c.begin(), c.end(), comp);
|
||||
}
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Returns true if there are no elements
|
||||
bool empty() const
|
||||
{
|
||||
return c.empty();
|
||||
}
|
||||
|
||||
//! Returns the number of elements
|
||||
size_type size() const
|
||||
{
|
||||
return c.size();
|
||||
}
|
||||
|
||||
//! Returns the head element
|
||||
const value_type& top() const
|
||||
{
|
||||
return c.front();
|
||||
}
|
||||
|
||||
//! Get start iterator
|
||||
const_iterator begin() const
|
||||
{
|
||||
return c.begin();
|
||||
}
|
||||
|
||||
//! Get end iterator
|
||||
const_iterator end() const
|
||||
{
|
||||
return c.end();
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
Container c;
|
||||
Compare comp;
|
||||
};
|
||||
|
||||
#endif
|
||||
97
lib/base/CSimpleEventQueueBuffer.cpp
Normal file
97
lib/base/CSimpleEventQueueBuffer.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 "CSimpleEventQueueBuffer.h"
|
||||
#include "CStopwatch.h"
|
||||
#include "CArch.h"
|
||||
|
||||
class CEventQueueTimer { };
|
||||
|
||||
//
|
||||
// CSimpleEventQueueBuffer
|
||||
//
|
||||
|
||||
CSimpleEventQueueBuffer::CSimpleEventQueueBuffer()
|
||||
{
|
||||
m_queueMutex = ARCH->newMutex();
|
||||
m_queueReadyCond = ARCH->newCondVar();
|
||||
m_queueReady = false;
|
||||
}
|
||||
|
||||
CSimpleEventQueueBuffer::~CSimpleEventQueueBuffer()
|
||||
{
|
||||
ARCH->closeCondVar(m_queueReadyCond);
|
||||
ARCH->closeMutex(m_queueMutex);
|
||||
}
|
||||
|
||||
void
|
||||
CSimpleEventQueueBuffer::waitForEvent(double timeout)
|
||||
{
|
||||
CArchMutexLock lock(m_queueMutex);
|
||||
CStopwatch timer(true);
|
||||
while (!m_queueReady) {
|
||||
double timeLeft = timeout;
|
||||
if (timeLeft >= 0.0) {
|
||||
timeLeft -= timer.getTime();
|
||||
if (timeLeft < 0.0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, timeLeft);
|
||||
}
|
||||
}
|
||||
|
||||
IEventQueueBuffer::Type
|
||||
CSimpleEventQueueBuffer::getEvent(CEvent&, UInt32& dataID)
|
||||
{
|
||||
CArchMutexLock lock(m_queueMutex);
|
||||
if (!m_queueReady) {
|
||||
return kNone;
|
||||
}
|
||||
dataID = m_queue.back();
|
||||
m_queue.pop_back();
|
||||
m_queueReady = !m_queue.empty();
|
||||
return kUser;
|
||||
}
|
||||
|
||||
bool
|
||||
CSimpleEventQueueBuffer::addEvent(UInt32 dataID)
|
||||
{
|
||||
CArchMutexLock lock(m_queueMutex);
|
||||
m_queue.push_front(dataID);
|
||||
if (!m_queueReady) {
|
||||
m_queueReady = true;
|
||||
ARCH->broadcastCondVar(m_queueReadyCond);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CSimpleEventQueueBuffer::isEmpty() const
|
||||
{
|
||||
CArchMutexLock lock(m_queueMutex);
|
||||
return !m_queueReady;
|
||||
}
|
||||
|
||||
CEventQueueTimer*
|
||||
CSimpleEventQueueBuffer::newTimer(double, bool) const
|
||||
{
|
||||
return new CEventQueueTimer;
|
||||
}
|
||||
|
||||
void
|
||||
CSimpleEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
|
||||
{
|
||||
delete timer;
|
||||
}
|
||||
49
lib/base/CSimpleEventQueueBuffer.h
Normal file
49
lib/base/CSimpleEventQueueBuffer.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 CSIMPLEEVENTQUEUEBUFFER_H
|
||||
#define CSIMPLEEVENTQUEUEBUFFER_H
|
||||
|
||||
#include "IEventQueueBuffer.h"
|
||||
#include "IArchMultithread.h"
|
||||
#include "stddeque.h"
|
||||
|
||||
//! In-memory event queue buffer
|
||||
/*!
|
||||
An event queue buffer provides a queue of events for an IEventQueue.
|
||||
*/
|
||||
class CSimpleEventQueueBuffer : public IEventQueueBuffer {
|
||||
public:
|
||||
CSimpleEventQueueBuffer();
|
||||
~CSimpleEventQueueBuffer();
|
||||
|
||||
// IEventQueueBuffer overrides
|
||||
virtual void waitForEvent(double timeout);
|
||||
virtual Type getEvent(CEvent& event, UInt32& dataID);
|
||||
virtual bool addEvent(UInt32 dataID);
|
||||
virtual bool isEmpty() const;
|
||||
virtual CEventQueueTimer*
|
||||
newTimer(double duration, bool oneShot) const;
|
||||
virtual void deleteTimer(CEventQueueTimer*) const;
|
||||
|
||||
private:
|
||||
typedef std::deque<UInt32> CEventDeque;
|
||||
|
||||
CArchMutex m_queueMutex;
|
||||
CArchCond m_queueReadyCond;
|
||||
bool m_queueReady;
|
||||
CEventDeque m_queue;
|
||||
};
|
||||
|
||||
#endif
|
||||
126
lib/base/CStopwatch.cpp
Normal file
126
lib/base/CStopwatch.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 "CStopwatch.h"
|
||||
#include "CArch.h"
|
||||
|
||||
//
|
||||
// CStopwatch
|
||||
//
|
||||
|
||||
CStopwatch::CStopwatch(bool triggered) :
|
||||
m_mark(0.0),
|
||||
m_triggered(triggered),
|
||||
m_stopped(triggered)
|
||||
{
|
||||
if (!triggered) {
|
||||
m_mark = ARCH->time();
|
||||
}
|
||||
}
|
||||
|
||||
CStopwatch::~CStopwatch()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
double
|
||||
CStopwatch::reset()
|
||||
{
|
||||
if (m_stopped) {
|
||||
const double dt = m_mark;
|
||||
m_mark = 0.0;
|
||||
return dt;
|
||||
}
|
||||
else {
|
||||
const double t = ARCH->time();
|
||||
const double dt = t - m_mark;
|
||||
m_mark = t;
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CStopwatch::stop()
|
||||
{
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// save the elapsed time
|
||||
m_mark = ARCH->time() - m_mark;
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
void
|
||||
CStopwatch::start()
|
||||
{
|
||||
m_triggered = false;
|
||||
if (!m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set the mark such that it reports the time elapsed at stop()
|
||||
m_mark = ARCH->time() - m_mark;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void
|
||||
CStopwatch::setTrigger()
|
||||
{
|
||||
stop();
|
||||
m_triggered = true;
|
||||
}
|
||||
|
||||
double
|
||||
CStopwatch::getTime()
|
||||
{
|
||||
if (m_triggered) {
|
||||
const double dt = m_mark;
|
||||
start();
|
||||
return dt;
|
||||
}
|
||||
else if (m_stopped) {
|
||||
return m_mark;
|
||||
}
|
||||
else {
|
||||
return ARCH->time() - m_mark;
|
||||
}
|
||||
}
|
||||
|
||||
CStopwatch::operator double()
|
||||
{
|
||||
return getTime();
|
||||
}
|
||||
|
||||
bool
|
||||
CStopwatch::isStopped() const
|
||||
{
|
||||
return m_stopped;
|
||||
}
|
||||
|
||||
double
|
||||
CStopwatch::getTime() const
|
||||
{
|
||||
if (m_stopped) {
|
||||
return m_mark;
|
||||
}
|
||||
else {
|
||||
return ARCH->time() - m_mark;
|
||||
}
|
||||
}
|
||||
|
||||
CStopwatch::operator double() const
|
||||
{
|
||||
return getTime();
|
||||
}
|
||||
108
lib/base/CStopwatch.h
Normal file
108
lib/base/CStopwatch.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CSTOPWATCH_H
|
||||
#define CSTOPWATCH_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
//! A timer class
|
||||
/*!
|
||||
This class measures time intervals. All time interval measurement
|
||||
should use this class.
|
||||
*/
|
||||
class CStopwatch {
|
||||
public:
|
||||
/*!
|
||||
The default constructor does an implicit reset() or setTrigger().
|
||||
If triggered == false then the clock starts ticking.
|
||||
*/
|
||||
CStopwatch(bool triggered = false);
|
||||
~CStopwatch();
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Reset the timer to zero
|
||||
/*!
|
||||
Set the start time to the current time, returning the time since
|
||||
the last reset. This does not remove the trigger if it's set nor
|
||||
does it start a stopped clock. If the clock is stopped then
|
||||
subsequent reset()'s will return 0.
|
||||
*/
|
||||
double reset();
|
||||
|
||||
//! Stop the timer
|
||||
/*!
|
||||
Stop the stopwatch. The time interval while stopped is not
|
||||
counted by the stopwatch. stop() does not remove the trigger.
|
||||
Has no effect if already stopped.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
//! Start the timer
|
||||
/*!
|
||||
Start the stopwatch. start() removes the trigger, even if the
|
||||
stopwatch was already started.
|
||||
*/
|
||||
void start();
|
||||
|
||||
//! Stop the timer and set the trigger
|
||||
/*!
|
||||
setTrigger() stops the clock like stop() except there's an
|
||||
implicit start() the next time (non-const) getTime() is called.
|
||||
This is useful when you want the clock to start the first time
|
||||
you check it.
|
||||
*/
|
||||
void setTrigger();
|
||||
|
||||
//! Get elapsed time
|
||||
/*!
|
||||
Returns the time since the last reset() (or calls reset() and
|
||||
returns zero if the trigger is set).
|
||||
*/
|
||||
double getTime();
|
||||
//! Same as getTime()
|
||||
operator double();
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Check if timer is stopped
|
||||
/*!
|
||||
Returns true if the stopwatch is stopped.
|
||||
*/
|
||||
bool isStopped() const;
|
||||
|
||||
// return the time since the last reset().
|
||||
//! Get elapsed time
|
||||
/*!
|
||||
Returns the time since the last reset(). This cannot trigger the
|
||||
stopwatch to start and will not clear the trigger.
|
||||
*/
|
||||
double getTime() const;
|
||||
//! Same as getTime() const
|
||||
operator double() const;
|
||||
//@}
|
||||
|
||||
private:
|
||||
double getClock() const;
|
||||
|
||||
private:
|
||||
double m_mark;
|
||||
bool m_triggered;
|
||||
bool m_stopped;
|
||||
};
|
||||
|
||||
#endif
|
||||
25
lib/base/CString.h
Normal file
25
lib/base/CString.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CSTRING_H
|
||||
#define CSTRING_H
|
||||
|
||||
#include "common.h"
|
||||
#include "stdstring.h"
|
||||
|
||||
// use standard C++ string class for our string class
|
||||
typedef std::string CString;
|
||||
|
||||
#endif
|
||||
|
||||
194
lib/base/CStringUtil.cpp
Normal file
194
lib/base/CStringUtil.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 "CStringUtil.h"
|
||||
#include "CArch.h"
|
||||
#include "common.h"
|
||||
#include "stdvector.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
|
||||
//
|
||||
// CStringUtil
|
||||
//
|
||||
|
||||
CString
|
||||
CStringUtil::format(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
CString result = vformat(fmt, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
CString
|
||||
CStringUtil::vformat(const char* fmt, va_list args)
|
||||
{
|
||||
// find highest indexed substitution and the locations of substitutions
|
||||
std::vector<size_t> pos;
|
||||
std::vector<size_t> width;
|
||||
std::vector<int> index;
|
||||
int maxIndex = 0;
|
||||
for (const char* scan = fmt; *scan != '\0'; ++scan) {
|
||||
if (*scan == '%') {
|
||||
++scan;
|
||||
if (*scan == '\0') {
|
||||
break;
|
||||
}
|
||||
else if (*scan == '%') {
|
||||
// literal
|
||||
index.push_back(0);
|
||||
pos.push_back(static_cast<int>(scan - 1 - fmt));
|
||||
width.push_back(2);
|
||||
}
|
||||
else if (*scan == '{') {
|
||||
// get argument index
|
||||
char* end;
|
||||
int i = static_cast<int>(strtol(scan + 1, &end, 10));
|
||||
if (*end != '}') {
|
||||
// invalid index -- ignore
|
||||
scan = end - 1;
|
||||
}
|
||||
else {
|
||||
index.push_back(i);
|
||||
pos.push_back(static_cast<int>(scan - 1 - fmt));
|
||||
width.push_back(static_cast<int>(end - scan + 2));
|
||||
if (i > maxIndex) {
|
||||
maxIndex = i;
|
||||
}
|
||||
scan = end;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// improper escape -- ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get args
|
||||
std::vector<const char*> value;
|
||||
std::vector<size_t> length;
|
||||
value.push_back("%");
|
||||
length.push_back(1);
|
||||
for (int i = 0; i < maxIndex; ++i) {
|
||||
const char* arg = va_arg(args, const char*);
|
||||
size_t len = strlen(arg);
|
||||
value.push_back(arg);
|
||||
length.push_back(len);
|
||||
}
|
||||
|
||||
// compute final length
|
||||
size_t resultLength = strlen(fmt);
|
||||
const int n = static_cast<int>(pos.size());
|
||||
for (int i = 0; i < n; ++i) {
|
||||
resultLength -= width[i];
|
||||
resultLength += length[index[i]];
|
||||
}
|
||||
|
||||
// substitute
|
||||
CString result;
|
||||
result.reserve(resultLength);
|
||||
size_t src = 0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
result.append(fmt + src, pos[i] - src);
|
||||
result.append(value[index[i]]);
|
||||
src = pos[i] + width[i];
|
||||
}
|
||||
result.append(fmt + src);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CString
|
||||
CStringUtil::print(const char* fmt, ...)
|
||||
{
|
||||
char tmp[1024];
|
||||
char* buffer = tmp;
|
||||
int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
|
||||
CString result;
|
||||
while (buffer != NULL) {
|
||||
// try printing into the buffer
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int n = ARCH->vsnprintf(buffer, len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
// if the buffer wasn't big enough then make it bigger and try again
|
||||
if (n < 0 || n > len) {
|
||||
if (buffer != tmp) {
|
||||
delete[] buffer;
|
||||
}
|
||||
len *= 2;
|
||||
buffer = new char[len];
|
||||
}
|
||||
|
||||
// if it was big enough then save the string and don't try again
|
||||
else {
|
||||
result = buffer;
|
||||
if (buffer != tmp) {
|
||||
delete[] buffer;
|
||||
}
|
||||
buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CStringUtil::CaselessCmp
|
||||
//
|
||||
|
||||
bool
|
||||
CStringUtil::CaselessCmp::cmpEqual(
|
||||
const CString::value_type& a,
|
||||
const CString::value_type& b)
|
||||
{
|
||||
// should use std::tolower but not in all versions of libstdc++ have it
|
||||
return tolower(a) == tolower(b);
|
||||
}
|
||||
|
||||
bool
|
||||
CStringUtil::CaselessCmp::cmpLess(
|
||||
const CString::value_type& a,
|
||||
const CString::value_type& b)
|
||||
{
|
||||
// should use std::tolower but not in all versions of libstdc++ have it
|
||||
return tolower(a) < tolower(b);
|
||||
}
|
||||
|
||||
bool
|
||||
CStringUtil::CaselessCmp::less(const CString& a, const CString& b)
|
||||
{
|
||||
return std::lexicographical_compare(
|
||||
a.begin(), a.end(),
|
||||
b.begin(), b.end(),
|
||||
&CStringUtil::CaselessCmp::cmpLess);
|
||||
}
|
||||
|
||||
bool
|
||||
CStringUtil::CaselessCmp::equal(const CString& a, const CString& b)
|
||||
{
|
||||
return !(less(a, b) || less(b, a));
|
||||
}
|
||||
|
||||
bool
|
||||
CStringUtil::CaselessCmp::operator()(const CString& a, const CString& b) const
|
||||
{
|
||||
return less(a, b);
|
||||
}
|
||||
77
lib/base/CStringUtil.h
Normal file
77
lib/base/CStringUtil.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CSTRINGUTIL_H
|
||||
#define CSTRINGUTIL_H
|
||||
|
||||
#include "CString.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
//! String utilities
|
||||
/*!
|
||||
This class provides various functions for string manipulation.
|
||||
*/
|
||||
class CStringUtil {
|
||||
public:
|
||||
//! Format positional arguments
|
||||
/*!
|
||||
Format a string using positional arguments. fmt has literal
|
||||
characters and conversion specifications introduced by `\%':
|
||||
- \c\%\% -- literal `\%'
|
||||
- \c\%{n} -- positional element n, n a positive integer, {} are literal
|
||||
|
||||
All arguments in the variable list are const char*. Positional
|
||||
elements are indexed from 1.
|
||||
*/
|
||||
static CString format(const char* fmt, ...);
|
||||
|
||||
//! Format positional arguments
|
||||
/*!
|
||||
Same as format() except takes va_list.
|
||||
*/
|
||||
static CString vformat(const char* fmt, va_list);
|
||||
|
||||
//! Print a string using printf-style formatting
|
||||
/*!
|
||||
Equivalent to printf() except the result is returned as a CString.
|
||||
*/
|
||||
static CString print(const char* fmt, ...);
|
||||
|
||||
//! Case-insensitive comparisons
|
||||
/*!
|
||||
This class provides case-insensitve comparison functions.
|
||||
*/
|
||||
class CaselessCmp {
|
||||
public:
|
||||
//! Same as less()
|
||||
bool operator()(const CString& a, const CString& b) const;
|
||||
|
||||
//! Returns true iff \c a is lexicographically less than \c b
|
||||
static bool less(const CString& a, const CString& b);
|
||||
|
||||
//! Returns true iff \c a is lexicographically equal to \c b
|
||||
static bool equal(const CString& a, const CString& b);
|
||||
|
||||
//! Returns true iff \c a is lexicographically less than \c b
|
||||
static bool cmpLess(const CString::value_type& a,
|
||||
const CString::value_type& b);
|
||||
|
||||
//! Returns true iff \c a is lexicographically equal to \c b
|
||||
static bool cmpEqual(const CString::value_type& a,
|
||||
const CString::value_type& b);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
779
lib/base/CUnicode.cpp
Normal file
779
lib/base/CUnicode.cpp
Normal file
@@ -0,0 +1,779 @@
|
||||
/*
|
||||
* 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 "CUnicode.h"
|
||||
#include "CArch.h"
|
||||
#include <string.h>
|
||||
|
||||
//
|
||||
// local utility functions
|
||||
//
|
||||
|
||||
inline
|
||||
static
|
||||
UInt16
|
||||
decode16(const UInt8* n, bool byteSwapped)
|
||||
{
|
||||
union x16 {
|
||||
UInt8 n8[2];
|
||||
UInt16 n16;
|
||||
} c;
|
||||
if (byteSwapped) {
|
||||
c.n8[0] = n[1];
|
||||
c.n8[1] = n[0];
|
||||
}
|
||||
else {
|
||||
c.n8[0] = n[0];
|
||||
c.n8[1] = n[1];
|
||||
}
|
||||
return c.n16;
|
||||
}
|
||||
|
||||
inline
|
||||
static
|
||||
UInt32
|
||||
decode32(const UInt8* n, bool byteSwapped)
|
||||
{
|
||||
union x32 {
|
||||
UInt8 n8[4];
|
||||
UInt32 n32;
|
||||
} c;
|
||||
if (byteSwapped) {
|
||||
c.n8[0] = n[3];
|
||||
c.n8[1] = n[2];
|
||||
c.n8[2] = n[1];
|
||||
c.n8[3] = n[0];
|
||||
}
|
||||
else {
|
||||
c.n8[0] = n[0];
|
||||
c.n8[1] = n[1];
|
||||
c.n8[2] = n[2];
|
||||
c.n8[3] = n[3];
|
||||
}
|
||||
return c.n32;
|
||||
}
|
||||
|
||||
inline
|
||||
static
|
||||
void
|
||||
resetError(bool* errors)
|
||||
{
|
||||
if (errors != NULL) {
|
||||
*errors = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
static
|
||||
void
|
||||
setError(bool* errors)
|
||||
{
|
||||
if (errors != NULL) {
|
||||
*errors = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CUnicode
|
||||
//
|
||||
|
||||
UInt32 CUnicode::s_invalid = 0x0000ffff;
|
||||
UInt32 CUnicode::s_replacement = 0x0000fffd;
|
||||
|
||||
bool
|
||||
CUnicode::isUTF8(const CString& src)
|
||||
{
|
||||
// convert and test each character
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
||||
for (UInt32 n = src.size(); n > 0; ) {
|
||||
if (fromUTF8(data, n) == s_invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF8ToUCS2(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// get size of input string and reserve some space in output
|
||||
UInt32 n = src.size();
|
||||
CString dst;
|
||||
dst.reserve(2 * n);
|
||||
|
||||
// convert each character
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
||||
while (n > 0) {
|
||||
UInt32 c = fromUTF8(data, n);
|
||||
if (c == s_invalid) {
|
||||
c = s_replacement;
|
||||
}
|
||||
else if (c >= 0x00010000) {
|
||||
setError(errors);
|
||||
c = s_replacement;
|
||||
}
|
||||
UInt16 ucs2 = static_cast<UInt16>(c);
|
||||
dst.append(reinterpret_cast<const char*>(&ucs2), 2);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF8ToUCS4(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// get size of input string and reserve some space in output
|
||||
UInt32 n = src.size();
|
||||
CString dst;
|
||||
dst.reserve(4 * n);
|
||||
|
||||
// convert each character
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
||||
while (n > 0) {
|
||||
UInt32 c = fromUTF8(data, n);
|
||||
if (c == s_invalid) {
|
||||
c = s_replacement;
|
||||
}
|
||||
dst.append(reinterpret_cast<const char*>(&c), 4);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF8ToUTF16(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// get size of input string and reserve some space in output
|
||||
UInt32 n = src.size();
|
||||
CString dst;
|
||||
dst.reserve(2 * n);
|
||||
|
||||
// convert each character
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
||||
while (n > 0) {
|
||||
UInt32 c = fromUTF8(data, n);
|
||||
if (c == s_invalid) {
|
||||
c = s_replacement;
|
||||
}
|
||||
else if (c >= 0x00110000) {
|
||||
setError(errors);
|
||||
c = s_replacement;
|
||||
}
|
||||
if (c < 0x00010000) {
|
||||
UInt16 ucs2 = static_cast<UInt16>(c);
|
||||
dst.append(reinterpret_cast<const char*>(&ucs2), 2);
|
||||
}
|
||||
else {
|
||||
c -= 0x00010000;
|
||||
UInt16 utf16h = static_cast<UInt16>((c >> 10) + 0xd800);
|
||||
UInt16 utf16l = static_cast<UInt16>((c & 0x03ff) + 0xdc00);
|
||||
dst.append(reinterpret_cast<const char*>(&utf16h), 2);
|
||||
dst.append(reinterpret_cast<const char*>(&utf16l), 2);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF8ToUTF32(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// get size of input string and reserve some space in output
|
||||
UInt32 n = src.size();
|
||||
CString dst;
|
||||
dst.reserve(4 * n);
|
||||
|
||||
// convert each character
|
||||
const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
|
||||
while (n > 0) {
|
||||
UInt32 c = fromUTF8(data, n);
|
||||
if (c == s_invalid) {
|
||||
c = s_replacement;
|
||||
}
|
||||
else if (c >= 0x00110000) {
|
||||
setError(errors);
|
||||
c = s_replacement;
|
||||
}
|
||||
dst.append(reinterpret_cast<const char*>(&c), 4);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF8ToText(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert to wide char
|
||||
UInt32 size;
|
||||
wchar_t* tmp = UTF8ToWideChar(src, size, errors);
|
||||
|
||||
// convert string to multibyte
|
||||
int len = ARCH->convStringWCToMB(NULL, tmp, size, errors);
|
||||
char* mbs = new char[len + 1];
|
||||
ARCH->convStringWCToMB(mbs, tmp, size, errors);
|
||||
CString text(mbs, len);
|
||||
|
||||
// clean up
|
||||
delete[] mbs;
|
||||
delete[] tmp;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UCS2ToUTF8(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert
|
||||
UInt32 n = src.size() >> 1;
|
||||
return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UCS4ToUTF8(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert
|
||||
UInt32 n = src.size() >> 2;
|
||||
return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF16ToUTF8(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert
|
||||
UInt32 n = src.size() >> 1;
|
||||
return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::UTF32ToUTF8(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert
|
||||
UInt32 n = src.size() >> 2;
|
||||
return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::textToUTF8(const CString& src, bool* errors)
|
||||
{
|
||||
// default to success
|
||||
resetError(errors);
|
||||
|
||||
// convert string to wide characters
|
||||
UInt32 n = src.size();
|
||||
int len = ARCH->convStringMBToWC(NULL, src.c_str(), n, errors);
|
||||
wchar_t* wcs = new wchar_t[len + 1];
|
||||
ARCH->convStringMBToWC(wcs, src.c_str(), n, errors);
|
||||
|
||||
// convert to UTF8
|
||||
CString utf8 = wideCharToUTF8(wcs, len, errors);
|
||||
|
||||
// clean up
|
||||
delete[] wcs;
|
||||
|
||||
return utf8;
|
||||
}
|
||||
|
||||
wchar_t*
|
||||
CUnicode::UTF8ToWideChar(const CString& src, UInt32& size, bool* errors)
|
||||
{
|
||||
// convert to platform's wide character encoding
|
||||
CString tmp;
|
||||
switch (ARCH->getWideCharEncoding()) {
|
||||
case IArchString::kUCS2:
|
||||
tmp = UTF8ToUCS2(src, errors);
|
||||
size = tmp.size() >> 1;
|
||||
break;
|
||||
|
||||
case IArchString::kUCS4:
|
||||
tmp = UTF8ToUCS4(src, errors);
|
||||
size = tmp.size() >> 2;
|
||||
break;
|
||||
|
||||
case IArchString::kUTF16:
|
||||
tmp = UTF8ToUTF16(src, errors);
|
||||
size = tmp.size() >> 1;
|
||||
break;
|
||||
|
||||
case IArchString::kUTF32:
|
||||
tmp = UTF8ToUTF32(src, errors);
|
||||
size = tmp.size() >> 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "unknown wide character encoding");
|
||||
}
|
||||
|
||||
// copy to a wchar_t array
|
||||
wchar_t* dst = new wchar_t[size];
|
||||
::memcpy(dst, tmp.data(), sizeof(wchar_t) * size);
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors)
|
||||
{
|
||||
// convert from platform's wide character encoding.
|
||||
// note -- this must include a wide nul character (independent of
|
||||
// the CString's nul character).
|
||||
switch (ARCH->getWideCharEncoding()) {
|
||||
case IArchString::kUCS2:
|
||||
return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
||||
|
||||
case IArchString::kUCS4:
|
||||
return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
||||
|
||||
case IArchString::kUTF16:
|
||||
return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
||||
|
||||
case IArchString::kUTF32:
|
||||
return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
|
||||
|
||||
default:
|
||||
assert(0 && "unknown wide character encoding");
|
||||
return CString();
|
||||
}
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
||||
{
|
||||
// make some space
|
||||
CString dst;
|
||||
dst.reserve(n);
|
||||
|
||||
// check if first character is 0xfffe or 0xfeff
|
||||
bool byteSwapped = false;
|
||||
if (n >= 1) {
|
||||
switch (decode16(data, false)) {
|
||||
case 0x0000feff:
|
||||
data += 2;
|
||||
--n;
|
||||
break;
|
||||
|
||||
case 0x0000fffe:
|
||||
byteSwapped = true;
|
||||
data += 2;
|
||||
--n;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert each character
|
||||
for (; n > 0; data += 2, --n) {
|
||||
UInt32 c = decode16(data, byteSwapped);
|
||||
toUTF8(dst, c, errors);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
||||
{
|
||||
// make some space
|
||||
CString dst;
|
||||
dst.reserve(n);
|
||||
|
||||
// check if first character is 0xfffe or 0xfeff
|
||||
bool byteSwapped = false;
|
||||
if (n >= 1) {
|
||||
switch (decode32(data, false)) {
|
||||
case 0x0000feff:
|
||||
data += 4;
|
||||
--n;
|
||||
break;
|
||||
|
||||
case 0x0000fffe:
|
||||
byteSwapped = true;
|
||||
data += 4;
|
||||
--n;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert each character
|
||||
for (; n > 0; data += 4, --n) {
|
||||
UInt32 c = decode32(data, byteSwapped);
|
||||
toUTF8(dst, c, errors);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
||||
{
|
||||
// make some space
|
||||
CString dst;
|
||||
dst.reserve(n);
|
||||
|
||||
// check if first character is 0xfffe or 0xfeff
|
||||
bool byteSwapped = false;
|
||||
if (n >= 1) {
|
||||
switch (decode16(data, false)) {
|
||||
case 0x0000feff:
|
||||
data += 2;
|
||||
--n;
|
||||
break;
|
||||
|
||||
case 0x0000fffe:
|
||||
byteSwapped = true;
|
||||
data += 2;
|
||||
--n;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert each character
|
||||
for (; n > 0; data += 2, --n) {
|
||||
UInt32 c = decode16(data, byteSwapped);
|
||||
if (c < 0x0000d800 || c > 0x0000dfff) {
|
||||
toUTF8(dst, c, errors);
|
||||
}
|
||||
else if (n == 1) {
|
||||
// error -- missing second word
|
||||
setError(errors);
|
||||
toUTF8(dst, s_replacement, NULL);
|
||||
}
|
||||
else if (c >= 0x0000d800 && c <= 0x0000dbff) {
|
||||
UInt32 c2 = decode16(data, byteSwapped);
|
||||
data += 2;
|
||||
--n;
|
||||
if (c2 < 0x0000dc00 || c2 > 0x0000dfff) {
|
||||
// error -- [d800,dbff] not followed by [dc00,dfff]
|
||||
setError(errors);
|
||||
toUTF8(dst, s_replacement, NULL);
|
||||
}
|
||||
else {
|
||||
c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000;
|
||||
toUTF8(dst, c, errors);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// error -- [dc00,dfff] without leading [d800,dbff]
|
||||
setError(errors);
|
||||
toUTF8(dst, s_replacement, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
CString
|
||||
CUnicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors)
|
||||
{
|
||||
// make some space
|
||||
CString dst;
|
||||
dst.reserve(n);
|
||||
|
||||
// check if first character is 0xfffe or 0xfeff
|
||||
bool byteSwapped = false;
|
||||
if (n >= 1) {
|
||||
switch (decode32(data, false)) {
|
||||
case 0x0000feff:
|
||||
data += 4;
|
||||
--n;
|
||||
break;
|
||||
|
||||
case 0x0000fffe:
|
||||
byteSwapped = true;
|
||||
data += 4;
|
||||
--n;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// convert each character
|
||||
for (; n > 0; data += 4, --n) {
|
||||
UInt32 c = decode32(data, byteSwapped);
|
||||
if (c >= 0x00110000) {
|
||||
setError(errors);
|
||||
c = s_replacement;
|
||||
}
|
||||
toUTF8(dst, c, errors);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
UInt32
|
||||
CUnicode::fromUTF8(const UInt8*& data, UInt32& n)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(n != 0);
|
||||
|
||||
// compute character encoding length, checking for overlong
|
||||
// sequences (i.e. characters that don't use the shortest
|
||||
// possible encoding).
|
||||
UInt32 size;
|
||||
if (data[0] < 0x80) {
|
||||
// 0xxxxxxx
|
||||
size = 1;
|
||||
}
|
||||
else if (data[0] < 0xc0) {
|
||||
// 10xxxxxx -- in the middle of a multibyte character. counts
|
||||
// as one invalid character.
|
||||
--n;
|
||||
++data;
|
||||
return s_invalid;
|
||||
}
|
||||
else if (data[0] < 0xe0) {
|
||||
// 110xxxxx
|
||||
size = 2;
|
||||
}
|
||||
else if (data[0] < 0xf0) {
|
||||
// 1110xxxx
|
||||
size = 3;
|
||||
}
|
||||
else if (data[0] < 0xf8) {
|
||||
// 11110xxx
|
||||
size = 4;
|
||||
}
|
||||
else if (data[0] < 0xfc) {
|
||||
// 111110xx
|
||||
size = 5;
|
||||
}
|
||||
else if (data[0] < 0xfe) {
|
||||
// 1111110x
|
||||
size = 6;
|
||||
}
|
||||
else {
|
||||
// invalid sequence. dunno how many bytes to skip so skip one.
|
||||
--n;
|
||||
++data;
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
// make sure we have enough data
|
||||
if (size > n) {
|
||||
data += n;
|
||||
n = 0;
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
// extract character
|
||||
UInt32 c;
|
||||
switch (size) {
|
||||
case 1:
|
||||
c = static_cast<UInt32>(data[0]);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
c = ((static_cast<UInt32>(data[0]) & 0x1f) << 6) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
c = ((static_cast<UInt32>(data[0]) & 0x0f) << 12) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
|
||||
((static_cast<UInt32>(data[2]) & 0x3f) );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
c = ((static_cast<UInt32>(data[0]) & 0x07) << 18) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) );
|
||||
break;
|
||||
|
||||
case 5:
|
||||
c = ((static_cast<UInt32>(data[0]) & 0x03) << 24) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 18) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) );
|
||||
break;
|
||||
|
||||
case 6:
|
||||
c = ((static_cast<UInt32>(data[0]) & 0x01) << 30) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 24) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 18) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
|
||||
((static_cast<UInt32>(data[1]) & 0x3f) );
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0 && "invalid size");
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
// check that all bytes after the first have the pattern 10xxxxxx.
|
||||
// truncated sequences are treated as a single malformed character.
|
||||
bool truncated = false;
|
||||
switch (size) {
|
||||
case 6:
|
||||
if ((data[5] & 0xc0) != 0x80) {
|
||||
truncated = true;
|
||||
size = 5;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case 5:
|
||||
if ((data[4] & 0xc0) != 0x80) {
|
||||
truncated = true;
|
||||
size = 4;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case 4:
|
||||
if ((data[3] & 0xc0) != 0x80) {
|
||||
truncated = true;
|
||||
size = 3;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case 3:
|
||||
if ((data[2] & 0xc0) != 0x80) {
|
||||
truncated = true;
|
||||
size = 2;
|
||||
}
|
||||
// fall through
|
||||
|
||||
case 2:
|
||||
if ((data[1] & 0xc0) != 0x80) {
|
||||
truncated = true;
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// update parameters
|
||||
data += size;
|
||||
n -= size;
|
||||
|
||||
// invalid if sequence was truncated
|
||||
if (truncated) {
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
// check for characters that didn't use the smallest possible encoding
|
||||
static UInt32 s_minChar[] = {
|
||||
0,
|
||||
0x00000000,
|
||||
0x00000080,
|
||||
0x00000800,
|
||||
0x00010000,
|
||||
0x00200000,
|
||||
0x04000000
|
||||
};
|
||||
if (c < s_minChar[size]) {
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
// check for characters not in ISO-10646
|
||||
if (c >= 0x0000d800 && c <= 0x0000dfff) {
|
||||
return s_invalid;
|
||||
}
|
||||
if (c >= 0x0000fffe && c <= 0x0000ffff) {
|
||||
return s_invalid;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
CUnicode::toUTF8(CString& dst, UInt32 c, bool* errors)
|
||||
{
|
||||
UInt8 data[6];
|
||||
|
||||
// handle characters outside the valid range
|
||||
if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) {
|
||||
setError(errors);
|
||||
c = s_replacement;
|
||||
}
|
||||
|
||||
// convert to UTF-8
|
||||
if (c < 0x00000080) {
|
||||
data[0] = static_cast<UInt8>(c);
|
||||
dst.append(reinterpret_cast<char*>(data), 1);
|
||||
}
|
||||
else if (c < 0x00000800) {
|
||||
data[0] = static_cast<UInt8>(((c >> 6) & 0x0000001f) + 0xc0);
|
||||
data[1] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
||||
dst.append(reinterpret_cast<char*>(data), 2);
|
||||
}
|
||||
else if (c < 0x00010000) {
|
||||
data[0] = static_cast<UInt8>(((c >> 12) & 0x0000000f) + 0xe0);
|
||||
data[1] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
||||
data[2] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
||||
dst.append(reinterpret_cast<char*>(data), 3);
|
||||
}
|
||||
else if (c < 0x00200000) {
|
||||
data[0] = static_cast<UInt8>(((c >> 18) & 0x00000007) + 0xf0);
|
||||
data[1] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
||||
data[2] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
||||
data[3] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
||||
dst.append(reinterpret_cast<char*>(data), 4);
|
||||
}
|
||||
else if (c < 0x04000000) {
|
||||
data[0] = static_cast<UInt8>(((c >> 24) & 0x00000003) + 0xf8);
|
||||
data[1] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
|
||||
data[2] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
||||
data[3] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
||||
data[4] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
||||
dst.append(reinterpret_cast<char*>(data), 5);
|
||||
}
|
||||
else if (c < 0x80000000) {
|
||||
data[0] = static_cast<UInt8>(((c >> 30) & 0x00000001) + 0xfc);
|
||||
data[1] = static_cast<UInt8>(((c >> 24) & 0x0000003f) + 0x80);
|
||||
data[2] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
|
||||
data[3] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
|
||||
data[4] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
|
||||
data[5] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
|
||||
dst.append(reinterpret_cast<char*>(data), 6);
|
||||
}
|
||||
else {
|
||||
assert(0 && "character out of range");
|
||||
}
|
||||
}
|
||||
143
lib/base/CUnicode.h
Normal file
143
lib/base/CUnicode.h
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CUNICODE_H
|
||||
#define CUNICODE_H
|
||||
|
||||
#include "CString.h"
|
||||
#include "BasicTypes.h"
|
||||
|
||||
//! Unicode utility functions
|
||||
/*!
|
||||
This class provides functions for converting between various Unicode
|
||||
encodings and the current locale encoding.
|
||||
*/
|
||||
class CUnicode {
|
||||
public:
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Test UTF-8 string for validity
|
||||
/*!
|
||||
Returns true iff the string contains a valid sequence of UTF-8
|
||||
encoded characters.
|
||||
*/
|
||||
static bool isUTF8(const CString&);
|
||||
|
||||
//! Convert from UTF-8 to UCS-2 encoding
|
||||
/*!
|
||||
Convert from UTF-8 to UCS-2. If errors is not NULL then *errors
|
||||
is set to true iff any character could not be encoded in UCS-2.
|
||||
Decoding errors do not set *errors.
|
||||
*/
|
||||
static CString UTF8ToUCS2(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-8 to UCS-4 encoding
|
||||
/*!
|
||||
Convert from UTF-8 to UCS-4. If errors is not NULL then *errors
|
||||
is set to true iff any character could not be encoded in UCS-4.
|
||||
Decoding errors do not set *errors.
|
||||
*/
|
||||
static CString UTF8ToUCS4(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-8 to UTF-16 encoding
|
||||
/*!
|
||||
Convert from UTF-8 to UTF-16. If errors is not NULL then *errors
|
||||
is set to true iff any character could not be encoded in UTF-16.
|
||||
Decoding errors do not set *errors.
|
||||
*/
|
||||
static CString UTF8ToUTF16(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-8 to UTF-32 encoding
|
||||
/*!
|
||||
Convert from UTF-8 to UTF-32. If errors is not NULL then *errors
|
||||
is set to true iff any character could not be encoded in UTF-32.
|
||||
Decoding errors do not set *errors.
|
||||
*/
|
||||
static CString UTF8ToUTF32(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-8 to the current locale encoding
|
||||
/*!
|
||||
Convert from UTF-8 to the current locale encoding. If errors is not
|
||||
NULL then *errors is set to true iff any character could not be encoded.
|
||||
Decoding errors do not set *errors.
|
||||
*/
|
||||
static CString UTF8ToText(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UCS-2 to UTF-8
|
||||
/*!
|
||||
Convert from UCS-2 to UTF-8. If errors is not NULL then *errors is
|
||||
set to true iff any character could not be decoded.
|
||||
*/
|
||||
static CString UCS2ToUTF8(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UCS-4 to UTF-8
|
||||
/*!
|
||||
Convert from UCS-4 to UTF-8. If errors is not NULL then *errors is
|
||||
set to true iff any character could not be decoded.
|
||||
*/
|
||||
static CString UCS4ToUTF8(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-16 to UTF-8
|
||||
/*!
|
||||
Convert from UTF-16 to UTF-8. If errors is not NULL then *errors is
|
||||
set to true iff any character could not be decoded.
|
||||
*/
|
||||
static CString UTF16ToUTF8(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from UTF-32 to UTF-8
|
||||
/*!
|
||||
Convert from UTF-32 to UTF-8. If errors is not NULL then *errors is
|
||||
set to true iff any character could not be decoded.
|
||||
*/
|
||||
static CString UTF32ToUTF8(const CString&, bool* errors = NULL);
|
||||
|
||||
//! Convert from the current locale encoding to UTF-8
|
||||
/*!
|
||||
Convert from the current locale encoding to UTF-8. If errors is not
|
||||
NULL then *errors is set to true iff any character could not be decoded.
|
||||
*/
|
||||
static CString textToUTF8(const CString&, bool* errors = NULL);
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
// convert UTF8 to wchar_t string (using whatever encoding is native
|
||||
// to the platform). caller must delete[] the returned string. the
|
||||
// string is *not* nul terminated; the length (in characters) is
|
||||
// returned in size.
|
||||
static wchar_t* UTF8ToWideChar(const CString&,
|
||||
UInt32& size, bool* errors);
|
||||
|
||||
// convert nul terminated wchar_t string (in platform's native
|
||||
// encoding) to UTF8.
|
||||
static CString wideCharToUTF8(const wchar_t*,
|
||||
UInt32 size, bool* errors);
|
||||
|
||||
// internal conversion to UTF8
|
||||
static CString doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors);
|
||||
static CString doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors);
|
||||
static CString doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors);
|
||||
static CString doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors);
|
||||
|
||||
// convert characters to/from UTF8
|
||||
static UInt32 fromUTF8(const UInt8*& src, UInt32& size);
|
||||
static void toUTF8(CString& dst, UInt32 c, bool* errors);
|
||||
|
||||
private:
|
||||
static UInt32 s_invalid;
|
||||
static UInt32 s_replacement;
|
||||
};
|
||||
|
||||
#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
|
||||
43
lib/base/IEventQueue.cpp
Normal file
43
lib/base/IEventQueue.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 "IEventQueue.h"
|
||||
|
||||
//
|
||||
// IEventQueue
|
||||
//
|
||||
|
||||
static int g_systemTarget = 0;
|
||||
IEventQueue* IEventQueue::s_instance = NULL;
|
||||
|
||||
void*
|
||||
IEventQueue::getSystemTarget()
|
||||
{
|
||||
// any unique arbitrary pointer will do
|
||||
return &g_systemTarget;
|
||||
}
|
||||
|
||||
IEventQueue*
|
||||
IEventQueue::getInstance()
|
||||
{
|
||||
assert(s_instance != NULL);
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void
|
||||
IEventQueue::setInstance(IEventQueue* instance)
|
||||
{
|
||||
assert(s_instance == NULL || instance == NULL);
|
||||
s_instance = instance;
|
||||
}
|
||||
213
lib/base/IEventQueue.h
Normal file
213
lib/base/IEventQueue.h
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 "CEvent.h"
|
||||
|
||||
#define EVENTQUEUE IEventQueue::getInstance()
|
||||
|
||||
class IEventJob;
|
||||
class IEventQueueBuffer;
|
||||
|
||||
// Opaque type for timer info. This is defined by subclasses of
|
||||
// IEventQueueBuffer.
|
||||
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
|
||||
//@{
|
||||
|
||||
//! Set the buffer
|
||||
/*!
|
||||
Replace the current event queue buffer. Any queued events are
|
||||
discarded. The queue takes ownership of the buffer.
|
||||
*/
|
||||
virtual void adoptBuffer(IEventQueueBuffer*) = 0;
|
||||
|
||||
//! 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. If \p target is NULL it uses the returned timer as
|
||||
the 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) = 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. If \p target is NULL it uses the returned
|
||||
timer as the target.
|
||||
*/
|
||||
virtual CEventQueueTimer*
|
||||
newOneShotTimer(double duration,
|
||||
void* target) = 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 for an event type
|
||||
/*!
|
||||
Registers an event handler for \p type and \p target. The \p handler
|
||||
is adopted. Any existing handler for the type,target pair is deleted.
|
||||
\c dispatchEvent() will invoke \p handler for any event for \p target
|
||||
of type \p type. If no such handler exists it will use the handler
|
||||
for \p target and type \p kUnknown if it exists.
|
||||
*/
|
||||
virtual void adoptHandler(CEvent::Type type,
|
||||
void* target, IEventJob* handler) = 0;
|
||||
|
||||
//! Unregister an event handler for an event type
|
||||
/*!
|
||||
Unregisters an event handler for the \p type, \p target pair and
|
||||
deletes it.
|
||||
*/
|
||||
virtual void removeHandler(CEvent::Type type, void* target) = 0;
|
||||
|
||||
//! Unregister all event handlers for an event target
|
||||
/*!
|
||||
Unregisters all event handlers for the \p target and deletes them.
|
||||
*/
|
||||
virtual void removeHandlers(void* target) = 0;
|
||||
|
||||
//! Creates a new event type
|
||||
/*!
|
||||
Returns a unique event type id.
|
||||
*/
|
||||
virtual CEvent::Type
|
||||
registerType(const char* name) = 0;
|
||||
|
||||
//! 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.
|
||||
*/
|
||||
virtual CEvent::Type
|
||||
registerTypeOnce(CEvent::Type& type,
|
||||
const char* name) = 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 the \p type, \p target pair
|
||||
if it exists, otherwise it returns NULL.
|
||||
*/
|
||||
virtual IEventJob* getHandler(CEvent::Type type, void* target) const = 0;
|
||||
|
||||
//! Get name for event
|
||||
/*!
|
||||
Returns the name for the event \p type. This is primarily for
|
||||
debugging.
|
||||
*/
|
||||
virtual const char* getTypeName(CEvent::Type type) = 0;
|
||||
|
||||
//! 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 IEventQueue* getInstance();
|
||||
|
||||
//@}
|
||||
|
||||
protected:
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Set the singleton instance
|
||||
/*!
|
||||
Sets the singleton instance of the event queue
|
||||
*/
|
||||
static void setInstance(IEventQueue*);
|
||||
|
||||
//@}
|
||||
|
||||
private:
|
||||
static IEventQueue* s_instance;
|
||||
};
|
||||
|
||||
#endif
|
||||
94
lib/base/IEventQueueBuffer.h
Normal file
94
lib/base/IEventQueueBuffer.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 IEVENTQUEUEBUFFER_H
|
||||
#define IEVENTQUEUEBUFFER_H
|
||||
|
||||
#include "IInterface.h"
|
||||
#include "BasicTypes.h"
|
||||
|
||||
class CEvent;
|
||||
class CEventQueueTimer;
|
||||
|
||||
//! Event queue buffer interface
|
||||
/*!
|
||||
An event queue buffer provides a queue of events for an IEventQueue.
|
||||
*/
|
||||
class IEventQueueBuffer : public IInterface {
|
||||
public:
|
||||
enum Type {
|
||||
kNone, //!< No event is available
|
||||
kSystem, //!< Event is a system event
|
||||
kUser //!< Event is a user event
|
||||
};
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Block waiting for an event
|
||||
/*!
|
||||
Wait for an event in the event queue buffer for up to \p timeout
|
||||
seconds.
|
||||
*/
|
||||
virtual void waitForEvent(double timeout) = 0;
|
||||
|
||||
//! Get the next event
|
||||
/*!
|
||||
Get the next event from the buffer. Return kNone if no event is
|
||||
available. If a system event is next, return kSystem and fill in
|
||||
event. The event data in a system event can point to a static
|
||||
buffer (because CEvent::deleteData() will not attempt to delete
|
||||
data in a kSystem event). Otherwise, return kUser and fill in
|
||||
\p dataID with the value passed to \c addEvent().
|
||||
*/
|
||||
virtual Type getEvent(CEvent& event, UInt32& dataID) = 0;
|
||||
|
||||
//! Post an event
|
||||
/*!
|
||||
Add the given event to the end of the queue buffer. This is a user
|
||||
event and \c getEvent() must be able to identify it as such and
|
||||
return \p dataID. This method must cause \c waitForEvent() to
|
||||
return at some future time if it's blocked waiting on an event.
|
||||
*/
|
||||
virtual bool addEvent(UInt32 dataID) = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Check if event queue buffer is empty
|
||||
/*!
|
||||
Return true iff the event queue buffer is empty.
|
||||
*/
|
||||
virtual bool isEmpty() const = 0;
|
||||
|
||||
//! Create a timer object
|
||||
/*!
|
||||
Create and return a timer object. The object is opaque and is
|
||||
used only by the buffer but it must be a valid object (i.e.
|
||||
not NULL).
|
||||
*/
|
||||
virtual CEventQueueTimer*
|
||||
newTimer(double duration, bool oneShot) const = 0;
|
||||
|
||||
//! Destroy a timer object
|
||||
/*!
|
||||
Destroy a timer object previously returned by \c newTimer().
|
||||
*/
|
||||
virtual void deleteTimer(CEventQueueTimer*) const = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
#endif
|
||||
30
lib/base/IJob.h
Normal file
30
lib/base/IJob.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef IJOB_H
|
||||
#define IJOB_H
|
||||
|
||||
#include "IInterface.h"
|
||||
|
||||
//! Job interface
|
||||
/*!
|
||||
A job is an interface for executing some function.
|
||||
*/
|
||||
class IJob : public IInterface {
|
||||
public:
|
||||
//! Run the job
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
81
lib/base/ILogOutputter.h
Normal file
81
lib/base/ILogOutputter.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ILOGOUTPUTTER_H
|
||||
#define ILOGOUTPUTTER_H
|
||||
|
||||
#include "IInterface.h"
|
||||
#include "CLog.h"
|
||||
|
||||
//! Outputter interface
|
||||
/*!
|
||||
Type of outputter interface. The logger performs all output through
|
||||
outputters. ILogOutputter overrides must not call any log functions
|
||||
directly or indirectly.
|
||||
*/
|
||||
class ILogOutputter : public IInterface {
|
||||
public:
|
||||
typedef CLog::ELevel ELevel;
|
||||
|
||||
//! @name manipulators
|
||||
//@{
|
||||
|
||||
//! Open the outputter
|
||||
/*!
|
||||
Opens the outputter for writing. Calling this method on an
|
||||
already open outputter must have no effect.
|
||||
*/
|
||||
virtual void open(const char* title) = 0;
|
||||
|
||||
//! Close the outputter
|
||||
/*!
|
||||
Close the outputter. Calling this method on an already closed
|
||||
outputter must have no effect.
|
||||
*/
|
||||
virtual void close() = 0;
|
||||
|
||||
//! Show the outputter
|
||||
/*!
|
||||
Causes the output to become visible. This generally only makes sense
|
||||
for a logger in a graphical user interface. Other implementations
|
||||
will do nothing. Iff \p showIfEmpty is \c false then the implementation
|
||||
may optionally only show the log if it's not empty.
|
||||
*/
|
||||
virtual void show(bool showIfEmpty) = 0;
|
||||
|
||||
//! Write a message with level
|
||||
/*!
|
||||
Writes \c message, which has the given \c level, to a log.
|
||||
If this method returns true then CLog will stop passing the
|
||||
message to all outputters in the outputter chain, otherwise
|
||||
it continues. Most implementations should return true.
|
||||
*/
|
||||
virtual bool write(ELevel level, const char* message) = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Returns the newline sequence for the outputter
|
||||
/*!
|
||||
Different outputters use different character sequences for newlines.
|
||||
This method returns the appropriate newline sequence for this
|
||||
outputter.
|
||||
*/
|
||||
virtual const char* getNewline() const = 0;
|
||||
|
||||
//@}
|
||||
};
|
||||
|
||||
#endif
|
||||
267
lib/base/LogOutputters.cpp
Normal file
267
lib/base/LogOutputters.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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 "LogOutputters.h"
|
||||
#include "CArch.h"
|
||||
|
||||
//
|
||||
// CStopLogOutputter
|
||||
//
|
||||
|
||||
CStopLogOutputter::CStopLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CStopLogOutputter::~CStopLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CStopLogOutputter::open(const char*)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CStopLogOutputter::close()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CStopLogOutputter::show(bool)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
CStopLogOutputter::write(ELevel, const char*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const char*
|
||||
CStopLogOutputter::getNewline() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CConsoleLogOutputter
|
||||
//
|
||||
|
||||
CConsoleLogOutputter::CConsoleLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CConsoleLogOutputter::~CConsoleLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CConsoleLogOutputter::open(const char* title)
|
||||
{
|
||||
ARCH->openConsole(title);
|
||||
}
|
||||
|
||||
void
|
||||
CConsoleLogOutputter::close()
|
||||
{
|
||||
ARCH->closeConsole();
|
||||
}
|
||||
|
||||
void
|
||||
CConsoleLogOutputter::show(bool showIfEmpty)
|
||||
{
|
||||
ARCH->showConsole(showIfEmpty);
|
||||
}
|
||||
|
||||
bool
|
||||
CConsoleLogOutputter::write(ELevel, const char* msg)
|
||||
{
|
||||
ARCH->writeConsole(msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char*
|
||||
CConsoleLogOutputter::getNewline() const
|
||||
{
|
||||
return ARCH->getNewlineForConsole();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CSystemLogOutputter
|
||||
//
|
||||
|
||||
CSystemLogOutputter::CSystemLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CSystemLogOutputter::~CSystemLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CSystemLogOutputter::open(const char* title)
|
||||
{
|
||||
ARCH->openLog(title);
|
||||
}
|
||||
|
||||
void
|
||||
CSystemLogOutputter::close()
|
||||
{
|
||||
ARCH->closeLog();
|
||||
}
|
||||
|
||||
void
|
||||
CSystemLogOutputter::show(bool showIfEmpty)
|
||||
{
|
||||
ARCH->showLog(showIfEmpty);
|
||||
}
|
||||
|
||||
bool
|
||||
CSystemLogOutputter::write(ELevel level, const char* msg)
|
||||
{
|
||||
IArchLog::ELevel archLogLevel;
|
||||
switch (level) {
|
||||
case CLog::kFATAL:
|
||||
case CLog::kERROR:
|
||||
archLogLevel = IArchLog::kERROR;
|
||||
break;
|
||||
|
||||
case CLog::kWARNING:
|
||||
archLogLevel = IArchLog::kWARNING;
|
||||
break;
|
||||
|
||||
case CLog::kNOTE:
|
||||
archLogLevel = IArchLog::kNOTE;
|
||||
break;
|
||||
|
||||
case CLog::kINFO:
|
||||
archLogLevel = IArchLog::kINFO;
|
||||
break;
|
||||
|
||||
default:
|
||||
archLogLevel = IArchLog::kDEBUG;
|
||||
break;
|
||||
|
||||
};
|
||||
ARCH->writeLog(archLogLevel, msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char*
|
||||
CSystemLogOutputter::getNewline() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CSystemLogger
|
||||
//
|
||||
|
||||
CSystemLogger::CSystemLogger(const char* title, bool blockConsole) :
|
||||
m_stop(NULL)
|
||||
{
|
||||
// redirect log messages
|
||||
if (blockConsole) {
|
||||
m_stop = new CStopLogOutputter;
|
||||
CLOG->insert(m_stop);
|
||||
}
|
||||
m_syslog = new CSystemLogOutputter;
|
||||
m_syslog->open(title);
|
||||
CLOG->insert(m_syslog);
|
||||
}
|
||||
|
||||
CSystemLogger::~CSystemLogger()
|
||||
{
|
||||
CLOG->remove(m_syslog);
|
||||
delete m_syslog;
|
||||
if (m_stop != NULL) {
|
||||
CLOG->remove(m_stop);
|
||||
delete m_stop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// CBufferedLogOutputter
|
||||
//
|
||||
|
||||
CBufferedLogOutputter::CBufferedLogOutputter(UInt32 maxBufferSize) :
|
||||
m_maxBufferSize(maxBufferSize)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CBufferedLogOutputter::~CBufferedLogOutputter()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
CBufferedLogOutputter::const_iterator
|
||||
CBufferedLogOutputter::begin() const
|
||||
{
|
||||
return m_buffer.begin();
|
||||
}
|
||||
|
||||
CBufferedLogOutputter::const_iterator
|
||||
CBufferedLogOutputter::end() const
|
||||
{
|
||||
return m_buffer.end();
|
||||
}
|
||||
|
||||
void
|
||||
CBufferedLogOutputter::open(const char*)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void
|
||||
CBufferedLogOutputter::close()
|
||||
{
|
||||
// remove all elements from the buffer
|
||||
m_buffer.clear();
|
||||
}
|
||||
|
||||
void
|
||||
CBufferedLogOutputter::show(bool)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
CBufferedLogOutputter::write(ELevel, const char* message)
|
||||
{
|
||||
while (m_buffer.size() >= m_maxBufferSize) {
|
||||
m_buffer.pop_front();
|
||||
}
|
||||
m_buffer.push_back(CString(message));
|
||||
return true;
|
||||
}
|
||||
|
||||
const char*
|
||||
CBufferedLogOutputter::getNewline() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
132
lib/base/LogOutputters.h
Normal file
132
lib/base/LogOutputters.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef LOGOUTPUTTERS_H
|
||||
#define LOGOUTPUTTERS_H
|
||||
|
||||
#include "BasicTypes.h"
|
||||
#include "ILogOutputter.h"
|
||||
#include "CString.h"
|
||||
#include "stddeque.h"
|
||||
|
||||
//! Stop traversing log chain outputter
|
||||
/*!
|
||||
This outputter performs no output and returns false from \c write(),
|
||||
causing the logger to stop traversing the outputter chain. Insert
|
||||
this to prevent already inserted outputters from writing.
|
||||
*/
|
||||
class CStopLogOutputter : public ILogOutputter {
|
||||
public:
|
||||
CStopLogOutputter();
|
||||
virtual ~CStopLogOutputter();
|
||||
|
||||
// ILogOutputter overrides
|
||||
virtual void open(const char* title);
|
||||
virtual void close();
|
||||
virtual void show(bool showIfEmpty);
|
||||
virtual bool write(ELevel level, const char* message);
|
||||
virtual const char* getNewline() const;
|
||||
};
|
||||
|
||||
//! Write log to console
|
||||
/*!
|
||||
This outputter writes output to the console. The level for each
|
||||
message is ignored.
|
||||
*/
|
||||
class CConsoleLogOutputter : public ILogOutputter {
|
||||
public:
|
||||
CConsoleLogOutputter();
|
||||
virtual ~CConsoleLogOutputter();
|
||||
|
||||
// ILogOutputter overrides
|
||||
virtual void open(const char* title);
|
||||
virtual void close();
|
||||
virtual void show(bool showIfEmpty);
|
||||
virtual bool write(ELevel level, const char* message);
|
||||
virtual const char* getNewline() const;
|
||||
};
|
||||
|
||||
//! Write log to system log
|
||||
/*!
|
||||
This outputter writes output to the system log.
|
||||
*/
|
||||
class CSystemLogOutputter : public ILogOutputter {
|
||||
public:
|
||||
CSystemLogOutputter();
|
||||
virtual ~CSystemLogOutputter();
|
||||
|
||||
// ILogOutputter overrides
|
||||
virtual void open(const char* title);
|
||||
virtual void close();
|
||||
virtual void show(bool showIfEmpty);
|
||||
virtual bool write(ELevel level, const char* message);
|
||||
virtual const char* getNewline() const;
|
||||
};
|
||||
|
||||
//! Write log to system log only
|
||||
/*!
|
||||
Creating an object of this type inserts a CStopLogOutputter followed
|
||||
by a CSystemLogOutputter into CLog. The destructor removes those
|
||||
outputters. Add one of these to any scope that needs to write to
|
||||
the system log (only) and restore the old outputters when exiting
|
||||
the scope.
|
||||
*/
|
||||
class CSystemLogger {
|
||||
public:
|
||||
CSystemLogger(const char* title, bool blockConsole);
|
||||
~CSystemLogger();
|
||||
|
||||
private:
|
||||
ILogOutputter* m_syslog;
|
||||
ILogOutputter* m_stop;
|
||||
};
|
||||
|
||||
//! Save log history
|
||||
/*!
|
||||
This outputter records the last N log messages.
|
||||
*/
|
||||
class CBufferedLogOutputter : public ILogOutputter {
|
||||
private:
|
||||
typedef std::deque<CString> CBuffer;
|
||||
|
||||
public:
|
||||
typedef CBuffer::const_iterator const_iterator;
|
||||
|
||||
CBufferedLogOutputter(UInt32 maxBufferSize);
|
||||
virtual ~CBufferedLogOutputter();
|
||||
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
||||
//! Get start of buffer
|
||||
const_iterator begin() const;
|
||||
|
||||
//! Get end of buffer
|
||||
const_iterator end() const;
|
||||
|
||||
//@}
|
||||
|
||||
// ILogOutputter overrides
|
||||
virtual void open(const char* title);
|
||||
virtual void close();
|
||||
virtual void show(bool showIfEmpty);
|
||||
virtual bool write(ELevel level, const char* message);
|
||||
virtual const char* getNewline() const;
|
||||
|
||||
private:
|
||||
UInt32 m_maxBufferSize;
|
||||
CBuffer m_buffer;
|
||||
};
|
||||
|
||||
#endif
|
||||
62
lib/base/Makefile.am
Normal file
62
lib/base/Makefile.am
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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.
|
||||
|
||||
## Process this file with automake to produce Makefile.in
|
||||
NULL =
|
||||
|
||||
EXTRA_DIST = \
|
||||
Makefile.win \
|
||||
$(NULL)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
Makefile.in \
|
||||
$(NULL)
|
||||
|
||||
noinst_LIBRARIES = libbase.a
|
||||
libbase_a_SOURCES = \
|
||||
CEvent.cpp \
|
||||
CEventQueue.cpp \
|
||||
CFunctionEventJob.cpp \
|
||||
CFunctionJob.cpp \
|
||||
CLog.cpp \
|
||||
CSimpleEventQueueBuffer.cpp \
|
||||
CStopwatch.cpp \
|
||||
CStringUtil.cpp \
|
||||
CUnicode.cpp \
|
||||
IEventQueue.cpp \
|
||||
LogOutputters.cpp \
|
||||
XBase.cpp \
|
||||
CEvent.h \
|
||||
CEventQueue.h \
|
||||
CFunctionEventJob.h \
|
||||
CFunctionJob.h \
|
||||
CLog.h \
|
||||
CPriorityQueue.h \
|
||||
CSimpleEventQueueBuffer.h \
|
||||
CStopwatch.h \
|
||||
CString.h \
|
||||
CStringUtil.h \
|
||||
CUnicode.h \
|
||||
IEventJob.h \
|
||||
IEventQueue.h \
|
||||
IEventQueueBuffer.h \
|
||||
IJob.h \
|
||||
ILogOutputter.h \
|
||||
LogOutputters.h \
|
||||
TMethodEventJob.h \
|
||||
TMethodJob.h \
|
||||
XBase.h \
|
||||
$(NULL)
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/lib/common \
|
||||
-I$(top_srcdir)/lib/arch \
|
||||
$(NULL)
|
||||
77
lib/base/Makefile.win
Normal file
77
lib/base/Makefile.win
Normal file
@@ -0,0 +1,77 @@
|
||||
# synergy -- mouse and keyboard sharing utility
|
||||
# Copyright (C) 2007 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.
|
||||
|
||||
LIB_BASE_SRC = lib\base
|
||||
LIB_BASE_DST = $(BUILD_DST)\$(LIB_BASE_SRC)
|
||||
LIB_BASE_LIB = "$(LIB_BASE_DST)\base.lib"
|
||||
LIB_BASE_CPP = \
|
||||
"CEvent.cpp" \
|
||||
"CEventQueue.cpp" \
|
||||
"CFunctionEventJob.cpp" \
|
||||
"CFunctionJob.cpp" \
|
||||
"CLog.cpp" \
|
||||
"CSimpleEventQueueBuffer.cpp" \
|
||||
"CStopwatch.cpp" \
|
||||
"CStringUtil.cpp" \
|
||||
"CUnicode.cpp" \
|
||||
"IEventQueue.cpp" \
|
||||
"LogOutputters.cpp" \
|
||||
"XBase.cpp" \
|
||||
$(NULL)
|
||||
LIB_BASE_OBJ = \
|
||||
"$(LIB_BASE_DST)\CEvent.obj" \
|
||||
"$(LIB_BASE_DST)\CEventQueue.obj" \
|
||||
"$(LIB_BASE_DST)\CFunctionEventJob.obj" \
|
||||
"$(LIB_BASE_DST)\CFunctionJob.obj" \
|
||||
"$(LIB_BASE_DST)\CLog.obj" \
|
||||
"$(LIB_BASE_DST)\CSimpleEventQueueBuffer.obj" \
|
||||
"$(LIB_BASE_DST)\CStopwatch.obj" \
|
||||
"$(LIB_BASE_DST)\CStringUtil.obj" \
|
||||
"$(LIB_BASE_DST)\CUnicode.obj" \
|
||||
"$(LIB_BASE_DST)\IEventQueue.obj" \
|
||||
"$(LIB_BASE_DST)\LogOutputters.obj" \
|
||||
"$(LIB_BASE_DST)\XBase.obj" \
|
||||
$(NULL)
|
||||
LIB_BASE_INC = \
|
||||
/I"lib\common" \
|
||||
/I"lib\arch" \
|
||||
$(NULL)
|
||||
|
||||
CPP_FILES = $(CPP_FILES) $(LIB_BASE_CPP)
|
||||
OBJ_FILES = $(OBJ_FILES) $(LIB_BASE_OBJ)
|
||||
LIB_FILES = $(LIB_FILES) $(LIB_BASE_LIB)
|
||||
|
||||
# Dependency rules
|
||||
$(LIB_BASE_OBJ): $(AUTODEP)
|
||||
!if EXIST($(LIB_BASE_DST)\deps.mak)
|
||||
!include $(LIB_BASE_DST)\deps.mak
|
||||
!endif
|
||||
|
||||
# Build rules. Use batch-mode rules if possible.
|
||||
!if DEFINED(_NMAKE_VER)
|
||||
{$(LIB_BASE_SRC)\}.cpp{$(LIB_BASE_DST)\}.obj::
|
||||
!else
|
||||
{$(LIB_BASE_SRC)\}.cpp{$(LIB_BASE_DST)\}.obj:
|
||||
!endif
|
||||
@$(ECHO) Compile in $(LIB_BASE_SRC)
|
||||
-@$(MKDIR) $(LIB_BASE_DST) 2>NUL:
|
||||
$(cpp) $(cppdebug) $(cppflags) $(cppvarsmt) /showIncludes \
|
||||
$(LIB_BASE_INC) \
|
||||
/Fo$(LIB_BASE_DST)\ \
|
||||
/Fd$(LIB_BASE_LIB:.lib=.pdb) \
|
||||
$< | $(AUTODEP) $(LIB_BASE_SRC) $(LIB_BASE_DST)
|
||||
$(LIB_BASE_LIB): $(LIB_BASE_OBJ)
|
||||
@$(ECHO) Link $(@F)
|
||||
$(implib) $(ildebug) $(ilflags) \
|
||||
/out:$@ \
|
||||
$**
|
||||
$(AUTODEP) $(LIB_BASE_SRC) $(LIB_BASE_DST) $(**:.obj=.d)
|
||||
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
|
||||
67
lib/base/TMethodJob.h
Normal file
67
lib/base/TMethodJob.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CMETHODJOB_H
|
||||
#define CMETHODJOB_H
|
||||
|
||||
#include "IJob.h"
|
||||
|
||||
//! Use a function as a job
|
||||
/*!
|
||||
A job class that invokes a member function.
|
||||
*/
|
||||
template <class T>
|
||||
class TMethodJob : public IJob {
|
||||
public:
|
||||
//! run() invokes \c object->method(arg)
|
||||
TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL);
|
||||
virtual ~TMethodJob();
|
||||
|
||||
// IJob overrides
|
||||
virtual void run();
|
||||
|
||||
private:
|
||||
T* m_object;
|
||||
void (T::*m_method)(void*);
|
||||
void* m_arg;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
TMethodJob<T>::TMethodJob(T* object, void (T::*method)(void*), void* arg) :
|
||||
m_object(object),
|
||||
m_method(method),
|
||||
m_arg(arg)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
TMethodJob<T>::~TMethodJob()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline
|
||||
void
|
||||
TMethodJob<T>::run()
|
||||
{
|
||||
if (m_object != NULL) {
|
||||
(m_object->*m_method)(m_arg);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
69
lib/base/XBase.cpp
Normal file
69
lib/base/XBase.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 "XBase.h"
|
||||
#include "CStringUtil.h"
|
||||
#include <cerrno>
|
||||
#include <cstdarg>
|
||||
|
||||
//
|
||||
// XBase
|
||||
//
|
||||
|
||||
XBase::XBase() :
|
||||
m_what()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
XBase::XBase(const CString& msg) :
|
||||
m_what(msg)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
XBase::~XBase()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const char*
|
||||
XBase::what() const
|
||||
{
|
||||
if (m_what.empty()) {
|
||||
m_what = getWhat();
|
||||
}
|
||||
return m_what.c_str();
|
||||
}
|
||||
|
||||
CString
|
||||
XBase::format(const char* /*id*/, const char* fmt, ...) const throw()
|
||||
{
|
||||
// FIXME -- lookup message string using id as an index. set
|
||||
// fmt to that string if it exists.
|
||||
|
||||
// format
|
||||
CString result;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
try {
|
||||
result = CStringUtil::vformat(fmt, args);
|
||||
}
|
||||
catch (...) {
|
||||
// ignore
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return result;
|
||||
}
|
||||
121
lib/base/XBase.h
Normal file
121
lib/base/XBase.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef XBASE_H
|
||||
#define XBASE_H
|
||||
|
||||
#include "CString.h"
|
||||
|
||||
//! Exception base class
|
||||
/*!
|
||||
This is the base class of most exception types.
|
||||
*/
|
||||
class XBase {
|
||||
public:
|
||||
//! Use getWhat() as the result of what()
|
||||
XBase();
|
||||
//! Use \c msg as the result of what()
|
||||
XBase(const CString& msg);
|
||||
virtual ~XBase();
|
||||
|
||||
//! Reason for exception
|
||||
virtual const char* what() const;
|
||||
|
||||
protected:
|
||||
//! Get a human readable string describing the exception
|
||||
virtual CString getWhat() const throw() = 0;
|
||||
|
||||
//! Format a string
|
||||
/*!
|
||||
Looks up a message format using \c id, using \c defaultFormat if
|
||||
no format can be found, then replaces positional parameters in
|
||||
the format string and returns the result.
|
||||
*/
|
||||
virtual CString format(const char* id,
|
||||
const char* defaultFormat, ...) const throw();
|
||||
|
||||
private:
|
||||
mutable CString m_what;
|
||||
};
|
||||
|
||||
/*!
|
||||
\def XBASE_SUBCLASS
|
||||
Convenience macro to subclass from XBase (or a subclass of it),
|
||||
providing the c'tor taking a const CString&. getWhat() is not
|
||||
declared.
|
||||
*/
|
||||
#define XBASE_SUBCLASS(name_, super_) \
|
||||
class name_ : public super_ { \
|
||||
public: \
|
||||
name_() : super_() { } \
|
||||
name_(const CString& msg) : super_(msg) { } \
|
||||
}
|
||||
|
||||
/*!
|
||||
\def XBASE_SUBCLASS
|
||||
Convenience macro to subclass from XBase (or a subclass of it),
|
||||
providing the c'tor taking a const CString&. getWhat() must be
|
||||
implemented.
|
||||
*/
|
||||
#define XBASE_SUBCLASS_WHAT(name_, super_) \
|
||||
class name_ : public super_ { \
|
||||
public: \
|
||||
name_() : super_() { } \
|
||||
name_(const CString& msg) : super_(msg) { } \
|
||||
\
|
||||
protected: \
|
||||
virtual CString getWhat() const throw(); \
|
||||
}
|
||||
|
||||
/*!
|
||||
\def XBASE_SUBCLASS_FORMAT
|
||||
Convenience macro to subclass from XBase (or a subclass of it),
|
||||
providing the c'tor taking a const CString&. what() is overridden
|
||||
to call getWhat() when first called; getWhat() can format the
|
||||
error message and can call what() to get the message passed to the
|
||||
c'tor.
|
||||
*/
|
||||
#define XBASE_SUBCLASS_FORMAT(name_, super_) \
|
||||
class name_ : public super_ { \
|
||||
private: \
|
||||
enum EState { kFirst, kFormat, kDone }; \
|
||||
\
|
||||
public: \
|
||||
name_() : super_(), m_state(kDone) { } \
|
||||
name_(const CString& msg) : super_(msg), m_state(kFirst) { } \
|
||||
\
|
||||
virtual const char* what() const \
|
||||
{ \
|
||||
if (m_state == kFirst) { \
|
||||
m_state = kFormat; \
|
||||
m_formatted = getWhat(); \
|
||||
m_state = kDone; \
|
||||
} \
|
||||
if (m_state == kDone) { \
|
||||
return m_formatted.c_str(); \
|
||||
} \
|
||||
else { \
|
||||
return super_::what(); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
protected: \
|
||||
virtual CString getWhat() const throw(); \
|
||||
\
|
||||
private: \
|
||||
mutable EState m_state; \
|
||||
mutable std::string m_formatted; \
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user