aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/base
diff options
context:
space:
mode:
authorLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
committerLibravatarUnit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
commit9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch)
treece8840148d8445055ba9e4f12263b2208f234c16 /src/lib/base
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
Diffstat (limited to 'src/lib/base')
-rw-r--r--src/lib/base/CMakeLists.txt28
-rw-r--r--src/lib/base/ELevel.h38
-rw-r--r--src/lib/base/Event.cpp100
-rw-r--r--src/lib/base/Event.h126
-rw-r--r--src/lib/base/EventQueue.cpp658
-rw-r--r--src/lib/base/EventQueue.h200
-rw-r--r--src/lib/base/EventTypes.cpp203
-rw-r--r--src/lib/base/EventTypes.h754
-rw-r--r--src/lib/base/FunctionEventJob.cpp44
-rw-r--r--src/lib/base/FunctionEventJob.h39
-rw-r--r--src/lib/base/FunctionJob.cpp43
-rw-r--r--src/lib/base/FunctionJob.h39
-rw-r--r--src/lib/base/IEventJob.h33
-rw-r--r--src/lib/base/IEventQueue.h251
-rw-r--r--src/lib/base/IEventQueueBuffer.h101
-rw-r--r--src/lib/base/IJob.h31
-rw-r--r--src/lib/base/ILogOutputter.h69
-rw-r--r--src/lib/base/Log.cpp309
-rw-r--r--src/lib/base/Log.h211
-rw-r--r--src/lib/base/NonBlockingStream.cpp60
-rw-r--r--src/lib/base/NonBlockingStream.h49
-rw-r--r--src/lib/base/PriorityQueue.h138
-rw-r--r--src/lib/base/SimpleEventQueueBuffer.cpp101
-rw-r--r--src/lib/base/SimpleEventQueueBuffer.h52
-rw-r--r--src/lib/base/Stopwatch.cpp130
-rw-r--r--src/lib/base/Stopwatch.h109
-rw-r--r--src/lib/base/String.cpp295
-rw-r--r--src/lib/base/String.h135
-rw-r--r--src/lib/base/TMethodEventJob.h71
-rw-r--r--src/lib/base/TMethodJob.h68
-rw-r--r--src/lib/base/Unicode.cpp784
-rw-r--r--src/lib/base/Unicode.h144
-rw-r--r--src/lib/base/XBase.cpp76
-rw-r--r--src/lib/base/XBase.h125
-rw-r--r--src/lib/base/log_outputters.cpp337
-rw-r--r--src/lib/base/log_outputters.h172
36 files changed, 6123 insertions, 0 deletions
diff --git a/src/lib/base/CMakeLists.txt b/src/lib/base/CMakeLists.txt
new file mode 100644
index 0000000..66ba5a6
--- /dev/null
+++ b/src/lib/base/CMakeLists.txt
@@ -0,0 +1,28 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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 LICENSE 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(base STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(base common)
+endif()
diff --git a/src/lib/base/ELevel.h b/src/lib/base/ELevel.h
new file mode 100644
index 0000000..ec0f94f
--- /dev/null
+++ b/src/lib/base/ELevel.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+//! Log levels
+/*!
+The logging priority levels in order of highest to lowest priority.
+*/
+enum ELevel {
+ kPRINT = -1, //!< For print only (no file or time)
+ 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 verbosity +1 debugging messages
+ kDEBUG2, //!< For verbosity +2 debugging messages
+ kDEBUG3, //!< For verbosity +3 debugging messages
+ kDEBUG4, //!< For verbosity +4 debugging messages
+ kDEBUG5 //!< For verbosity +5 debugging messages
+};
diff --git a/src/lib/base/Event.cpp b/src/lib/base/Event.cpp
new file mode 100644
index 0000000..f2c1a12
--- /dev/null
+++ b/src/lib/base/Event.cpp
@@ -0,0 +1,100 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/Event.h"
+#include "base/EventQueue.h"
+
+//
+// Event
+//
+
+Event::Event() :
+ m_type(kUnknown),
+ m_target(NULL),
+ m_data(NULL),
+ m_flags(0),
+ m_dataObject(nullptr)
+{
+ // do nothing
+}
+
+Event::Event(Type type, void* target, void* data, Flags flags) :
+ m_type(type),
+ m_target(target),
+ m_data(data),
+ m_flags(flags),
+ m_dataObject(nullptr)
+{
+ // do nothing
+}
+
+Event::Type
+Event::getType() const
+{
+ return m_type;
+}
+
+void*
+Event::getTarget() const
+{
+ return m_target;
+}
+
+void*
+Event::getData() const
+{
+ return m_data;
+}
+
+EventData*
+Event::getDataObject() const
+{
+ return m_dataObject;
+}
+
+Event::Flags
+Event::getFlags() const
+{
+ return m_flags;
+}
+
+void
+Event::deleteData(const Event& event)
+{
+ switch (event.getType()) {
+ case kUnknown:
+ case kQuit:
+ case kSystem:
+ case kTimer:
+ break;
+
+ default:
+ if ((event.getFlags() & kDontFreeData) == 0) {
+ free(event.getData());
+ delete event.getDataObject();
+ }
+ break;
+ }
+}
+
+void
+Event::setDataObject(EventData* dataObject)
+{
+ assert(m_dataObject == nullptr);
+ m_dataObject = dataObject;
+}
diff --git a/src/lib/base/Event.h b/src/lib/base/Event.h
new file mode 100644
index 0000000..2741813
--- /dev/null
+++ b/src/lib/base/Event.h
@@ -0,0 +1,126 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/basic_types.h"
+#include "common/stdmap.h"
+
+class EventData {
+public:
+ EventData() { }
+ virtual ~EventData() { }
+};
+
+//! Event
+/*!
+A \c Event holds an event type and a pointer to event data.
+*/
+class Event {
+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
+ };
+
+ Event();
+
+ //! Create \c Event with data (POD)
+ /*!
+ 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. For non-POD (normal C++ objects
+ use \c setDataObject().
+ \p target is the intended recipient of the event.
+ \p flags is any combination of \c Flags.
+ */
+ Event(Type type, void* target = NULL, void* data = NULL,
+ Flags flags = kNone);
+
+ //! @name manipulators
+ //@{
+
+ //! Release event data
+ /*!
+ Deletes event data for the given event (using free()).
+ */
+ static void deleteData(const Event&);
+
+ //! Set data (non-POD)
+ /*!
+ Set non-POD (non plain old data), where delete is called when the event
+ is deleted, and the destructor is called.
+ */
+ void setDataObject(EventData* dataObject);
+
+ //@}
+ //! @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 (POD).
+ /*!
+ Returns the event data (POD).
+ */
+ void* getData() const;
+
+ //! Get the event data (non-POD)
+ /*!
+ Returns the event data (non-POD). The difference between this and
+ \c getData() is that when delete is called on this data, so non-POD
+ (non plain old data) dtor is called.
+ */
+ EventData* getDataObject() const;
+
+ //! Get event flags
+ /*!
+ Returns the event flags.
+ */
+ Flags getFlags() const;
+
+ //@}
+
+private:
+ Type m_type;
+ void* m_target;
+ void* m_data;
+ Flags m_flags;
+ EventData* m_dataObject;
+};
diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp
new file mode 100644
index 0000000..b17e35b
--- /dev/null
+++ b/src/lib/base/EventQueue.cpp
@@ -0,0 +1,658 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/EventQueue.h"
+
+#include "mt/Mutex.h"
+#include "mt/Lock.h"
+#include "arch/Arch.h"
+#include "base/SimpleEventQueueBuffer.h"
+#include "base/Stopwatch.h"
+#include "base/IEventJob.h"
+#include "base/EventTypes.h"
+#include "base/Log.h"
+#include "base/XBase.h"
+#include "../gui/src/ShutdownCh.h"
+
+EVENT_TYPE_ACCESSOR(Client)
+EVENT_TYPE_ACCESSOR(IStream)
+EVENT_TYPE_ACCESSOR(IpcClient)
+EVENT_TYPE_ACCESSOR(IpcClientProxy)
+EVENT_TYPE_ACCESSOR(IpcServer)
+EVENT_TYPE_ACCESSOR(IpcServerProxy)
+EVENT_TYPE_ACCESSOR(IDataSocket)
+EVENT_TYPE_ACCESSOR(IListenSocket)
+EVENT_TYPE_ACCESSOR(ISocket)
+EVENT_TYPE_ACCESSOR(OSXScreen)
+EVENT_TYPE_ACCESSOR(ClientListener)
+EVENT_TYPE_ACCESSOR(ClientProxy)
+EVENT_TYPE_ACCESSOR(ClientProxyUnknown)
+EVENT_TYPE_ACCESSOR(Server)
+EVENT_TYPE_ACCESSOR(ServerApp)
+EVENT_TYPE_ACCESSOR(IKeyState)
+EVENT_TYPE_ACCESSOR(IPrimaryScreen)
+EVENT_TYPE_ACCESSOR(IScreen)
+EVENT_TYPE_ACCESSOR(Clipboard)
+EVENT_TYPE_ACCESSOR(File)
+
+// interrupt handler. this just adds a quit event to the queue.
+static
+void
+interrupt(Arch::ESignal, void* data)
+{
+ EventQueue* events = static_cast<EventQueue*>(data);
+ events->addEvent(Event(Event::kQuit));
+}
+
+
+//
+// EventQueue
+//
+
+EventQueue::EventQueue() :
+ m_systemTarget(0),
+ m_nextType(Event::kLast),
+ m_typesForClient(NULL),
+ m_typesForIStream(NULL),
+ m_typesForIpcClient(NULL),
+ m_typesForIpcClientProxy(NULL),
+ m_typesForIpcServer(NULL),
+ m_typesForIpcServerProxy(NULL),
+ m_typesForIDataSocket(NULL),
+ m_typesForIListenSocket(NULL),
+ m_typesForISocket(NULL),
+ m_typesForOSXScreen(NULL),
+ m_typesForClientListener(NULL),
+ m_typesForClientProxy(NULL),
+ m_typesForClientProxyUnknown(NULL),
+ m_typesForServer(NULL),
+ m_typesForServerApp(NULL),
+ m_typesForIKeyState(NULL),
+ m_typesForIPrimaryScreen(NULL),
+ m_typesForIScreen(NULL),
+ m_typesForClipboard(NULL),
+ m_typesForFile(NULL),
+ m_readyMutex(new Mutex),
+ m_readyCondVar(new CondVar<bool>(m_readyMutex, false))
+{
+ m_mutex = ARCH->newMutex();
+ ARCH->setSignalHandler(Arch::kINTERRUPT, &interrupt, this);
+ ARCH->setSignalHandler(Arch::kTERMINATE, &interrupt, this);
+ m_buffer = new SimpleEventQueueBuffer;
+}
+
+EventQueue::~EventQueue()
+{
+ delete m_buffer;
+ delete m_readyCondVar;
+ delete m_readyMutex;
+
+ ARCH->setSignalHandler(Arch::kINTERRUPT, NULL, NULL);
+ ARCH->setSignalHandler(Arch::kTERMINATE, NULL, NULL);
+ ARCH->closeMutex(m_mutex);
+}
+
+void
+EventQueue::loop()
+{
+ m_buffer->init();
+ {
+ Lock lock(m_readyMutex);
+ *m_readyCondVar = true;
+ m_readyCondVar->signal();
+ }
+ LOG((CLOG_DEBUG "event queue is ready"));
+ while (!m_pending.empty()) {
+ LOG((CLOG_DEBUG "add pending events to buffer"));
+ Event& event = m_pending.front();
+ addEventToBuffer(event);
+ m_pending.pop();
+ }
+
+ Event event;
+ getEvent(event);
+ while (event.getType() != Event::kQuit) {
+ dispatchEvent(event);
+ Event::deleteData(event);
+ getEvent(event);
+ }
+}
+
+Event::Type
+EventQueue::registerTypeOnce(Event::Type& type, const char* name)
+{
+ ArchMutexLock lock(m_mutex);
+ if (type == Event::kUnknown) {
+ m_typeMap.insert(std::make_pair(m_nextType, name));
+ m_nameMap.insert(std::make_pair(name, m_nextType));
+ LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
+ type = m_nextType++;
+ }
+ return type;
+}
+
+const char*
+EventQueue::getTypeName(Event::Type type)
+{
+ switch (type) {
+ case Event::kUnknown:
+ return "nil";
+
+ case Event::kQuit:
+ return "quit";
+
+ case Event::kSystem:
+ return "system";
+
+ case Event::kTimer:
+ return "timer";
+
+ default:
+ TypeMap::const_iterator i = m_typeMap.find(type);
+ if (i == m_typeMap.end()) {
+ return "<unknown>";
+ }
+ else {
+ return i->second;
+ }
+ }
+}
+
+void
+EventQueue::adoptBuffer(IEventQueueBuffer* buffer)
+{
+ ArchMutexLock lock(m_mutex);
+
+ LOG((CLOG_DEBUG "adopting new buffer"));
+
+ if (m_events.size() != 0) {
+ // this can come as a nasty surprise to programmers expecting
+ // their events to be raised, only to have them deleted.
+ LOG((CLOG_DEBUG "discarding %d event(s)", m_events.size()));
+ }
+
+ // discard old buffer and old events
+ delete m_buffer;
+ for (EventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
+ Event::deleteData(i->second);
+ }
+ m_events.clear();
+ m_oldEventIDs.clear();
+
+ // use new buffer
+ m_buffer = buffer;
+ if (m_buffer == NULL) {
+ m_buffer = new SimpleEventQueueBuffer;
+ }
+}
+
+bool
+EventQueue::parent_requests_shutdown() const
+{
+ char ch;
+ return m_parentStream.try_read_char(ch) && ch == ShutdownCh;
+}
+
+bool
+EventQueue::getEvent(Event& event, double timeout)
+{
+ Stopwatch timer(true);
+retry:
+ // before handling any events make sure we don't need to shutdown
+ if (parent_requests_shutdown()) {
+ event = Event(Event::kQuit);
+ return false;
+ }
+ // 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:
+ {
+ ArchMutexLock lock(m_mutex);
+ event = removeEvent(dataID);
+ return true;
+ }
+
+ default:
+ assert(0 && "invalid event type");
+ return false;
+ }
+}
+
+bool
+EventQueue::dispatchEvent(const Event& event)
+{
+ void* target = event.getTarget();
+ IEventJob* job = getHandler(event.getType(), target);
+ if (job == NULL) {
+ job = getHandler(Event::kUnknown, target);
+ }
+ if (job != NULL) {
+ job->run(event);
+ return true;
+ }
+ return false;
+}
+
+void
+EventQueue::addEvent(const Event& event)
+{
+ // discard bogus event types
+ switch (event.getType()) {
+ case Event::kUnknown:
+ case Event::kSystem:
+ case Event::kTimer:
+ return;
+
+ default:
+ break;
+ }
+
+ if ((event.getFlags() & Event::kDeliverImmediately) != 0) {
+ dispatchEvent(event);
+ Event::deleteData(event);
+ }
+ else if (!(*m_readyCondVar)) {
+ m_pending.push(event);
+ }
+ else {
+ addEventToBuffer(event);
+ }
+}
+
+void
+EventQueue::addEventToBuffer(const Event& event)
+{
+ ArchMutexLock 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);
+ Event::deleteData(event);
+ }
+}
+
+EventQueueTimer*
+EventQueue::newTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ EventQueueTimer* timer = m_buffer->newTimer(duration, false);
+ if (target == NULL) {
+ target = timer;
+ }
+ ArchMutexLock 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(Timer(timer, duration,
+ duration + m_time.getTime(), target, false));
+ return timer;
+}
+
+EventQueueTimer*
+EventQueue::newOneShotTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ EventQueueTimer* timer = m_buffer->newTimer(duration, true);
+ if (target == NULL) {
+ target = timer;
+ }
+ ArchMutexLock 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(Timer(timer, duration,
+ duration + m_time.getTime(), target, true));
+ return timer;
+}
+
+void
+EventQueue::deleteTimer(EventQueueTimer* timer)
+{
+ ArchMutexLock lock(m_mutex);
+ for (TimerQueue::iterator index = m_timerQueue.begin();
+ index != m_timerQueue.end(); ++index) {
+ if (index->getTimer() == timer) {
+ m_timerQueue.erase(index);
+ break;
+ }
+ }
+ Timers::iterator index = m_timers.find(timer);
+ if (index != m_timers.end()) {
+ m_timers.erase(index);
+ }
+ m_buffer->deleteTimer(timer);
+}
+
+void
+EventQueue::adoptHandler(Event::Type type, void* target, IEventJob* handler)
+{
+ ArchMutexLock lock(m_mutex);
+ IEventJob*& job = m_handlers[target][type];
+ delete job;
+ job = handler;
+}
+
+void
+EventQueue::removeHandler(Event::Type type, void* target)
+{
+ IEventJob* handler = NULL;
+ {
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ TypeHandlerTable& typeHandlers = index->second;
+ TypeHandlerTable::iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ handler = index2->second;
+ typeHandlers.erase(index2);
+ }
+ }
+ }
+ delete handler;
+}
+
+void
+EventQueue::removeHandlers(void* target)
+{
+ std::vector<IEventJob*> handlers;
+ {
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ // copy to handlers array and clear table for target
+ TypeHandlerTable& typeHandlers = index->second;
+ for (TypeHandlerTable::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
+EventQueue::isEmpty() const
+{
+ return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
+}
+
+IEventJob*
+EventQueue::getHandler(Event::Type type, void* target) const
+{
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::const_iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ const TypeHandlerTable& typeHandlers = index->second;
+ TypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ return index2->second;
+ }
+ }
+ return NULL;
+}
+
+UInt32
+EventQueue::saveEvent(const Event& 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;
+}
+
+Event
+EventQueue::removeEvent(UInt32 eventID)
+{
+ // look up id
+ EventTable::iterator index = m_events.find(eventID);
+ if (index == m_events.end()) {
+ return Event();
+ }
+
+ // get data
+ Event event = index->second;
+ m_events.erase(index);
+
+ // save old id for reuse
+ m_oldEventIDs.push_back(eventID);
+
+ return event;
+}
+
+bool
+EventQueue::hasTimerExpired(Event& 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 (TimerQueue::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
+ Timer timer = m_timerQueue.top();
+ m_timerQueue.pop();
+
+ // prepare event and reset the timer's clock
+ timer.fillEvent(m_timerEvent);
+ event = Event(Event::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
+EventQueue::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();
+}
+
+Event::Type
+EventQueue::getRegisteredType(const String& name) const
+{
+ NameMap::const_iterator found = m_nameMap.find(name);
+ if (found != m_nameMap.end())
+ return found->second;
+
+ return Event::kUnknown;
+}
+
+void*
+EventQueue::getSystemTarget()
+{
+ // any unique arbitrary pointer will do
+ return &m_systemTarget;
+}
+
+void
+EventQueue::waitForReady() const
+{
+ double timeout = ARCH->time() + 10;
+ Lock lock(m_readyMutex);
+
+ while (!m_readyCondVar->wait()) {
+ if (ARCH->time() > timeout) {
+ throw std::runtime_error("event queue is not ready within 5 sec");
+ }
+ }
+}
+
+//
+// EventQueue::Timer
+//
+
+EventQueue::Timer::Timer(EventQueueTimer* 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);
+}
+
+EventQueue::Timer::~Timer()
+{
+ // do nothing
+}
+
+void
+EventQueue::Timer::reset()
+{
+ m_time = m_timeout;
+}
+
+EventQueue::Timer&
+EventQueue::Timer::operator-=(double dt)
+{
+ m_time -= dt;
+ return *this;
+}
+
+EventQueue::Timer::operator double() const
+{
+ return m_time;
+}
+
+bool
+EventQueue::Timer::isOneShot() const
+{
+ return m_oneShot;
+}
+
+EventQueueTimer*
+EventQueue::Timer::getTimer() const
+{
+ return m_timer;
+}
+
+void*
+EventQueue::Timer::getTarget() const
+{
+ return m_target;
+}
+
+void
+EventQueue::Timer::fillEvent(TimerEvent& 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
+EventQueue::Timer::operator<(const Timer& t) const
+{
+ return m_time < t.m_time;
+}
diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h
new file mode 100644
index 0000000..97e7fba
--- /dev/null
+++ b/src/lib/base/EventQueue.h
@@ -0,0 +1,200 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mt/CondVar.h"
+#include "arch/IArchMultithread.h"
+#include "base/IEventQueue.h"
+#include "base/Event.h"
+#include "base/PriorityQueue.h"
+#include "base/Stopwatch.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "base/NonBlockingStream.h"
+
+#include <queue>
+
+class Mutex;
+
+//! Event queue
+/*!
+An event queue that implements the platform independent parts and
+delegates the platform dependent parts to a subclass.
+*/
+class EventQueue : public IEventQueue {
+public:
+ EventQueue();
+ virtual ~EventQueue();
+
+ // IEventQueue overrides
+ virtual void loop();
+ virtual void adoptBuffer(IEventQueueBuffer*);
+ virtual bool getEvent(Event& event, double timeout = -1.0);
+ virtual bool dispatchEvent(const Event& event);
+ virtual void addEvent(const Event& event);
+ virtual EventQueueTimer*
+ newTimer(double duration, void* target);
+ virtual EventQueueTimer*
+ newOneShotTimer(double duration, void* target);
+ virtual void deleteTimer(EventQueueTimer*);
+ virtual void adoptHandler(Event::Type type,
+ void* target, IEventJob* handler);
+ virtual void removeHandler(Event::Type type, void* target);
+ virtual void removeHandlers(void* target);
+ virtual Event::Type
+ registerTypeOnce(Event::Type& type, const char* name);
+ virtual bool isEmpty() const;
+ virtual IEventJob* getHandler(Event::Type type, void* target) const;
+ virtual const char* getTypeName(Event::Type type);
+ virtual Event::Type
+ getRegisteredType(const String& name) const;
+ void* getSystemTarget();
+ virtual void waitForReady() const;
+
+private:
+ UInt32 saveEvent(const Event& event);
+ Event removeEvent(UInt32 eventID);
+ bool hasTimerExpired(Event& event);
+ double getNextTimerTimeout() const;
+ void addEventToBuffer(const Event& event);
+ bool parent_requests_shutdown() const;
+
+private:
+ class Timer {
+ public:
+ Timer(EventQueueTimer*, double timeout, double initialTime,
+ void* target, bool oneShot);
+ ~Timer();
+
+ void reset();
+
+ Timer& operator-=(double);
+
+ operator double() const;
+
+ bool isOneShot() const;
+ EventQueueTimer*
+ getTimer() const;
+ void* getTarget() const;
+ void fillEvent(TimerEvent&) const;
+
+ bool operator<(const Timer&) const;
+
+ private:
+ EventQueueTimer* m_timer;
+ double m_timeout;
+ void* m_target;
+ bool m_oneShot;
+ double m_time;
+ };
+
+ typedef std::set<EventQueueTimer*> Timers;
+ typedef PriorityQueue<Timer> TimerQueue;
+ typedef std::map<UInt32, Event> EventTable;
+ typedef std::vector<UInt32> EventIDList;
+ typedef std::map<Event::Type, const char*> TypeMap;
+ typedef std::map<String, Event::Type> NameMap;
+ typedef std::map<Event::Type, IEventJob*> TypeHandlerTable;
+ typedef std::map<void*, TypeHandlerTable> HandlerTable;
+
+ int m_systemTarget;
+ ArchMutex m_mutex;
+
+ // registered events
+ Event::Type m_nextType;
+ TypeMap m_typeMap;
+ NameMap m_nameMap;
+
+ // buffer of events
+ IEventQueueBuffer* m_buffer;
+
+ // saved events
+ EventTable m_events;
+ EventIDList m_oldEventIDs;
+
+ // timers
+ Stopwatch m_time;
+ Timers m_timers;
+ TimerQueue m_timerQueue;
+ TimerEvent m_timerEvent;
+
+ // event handlers
+ HandlerTable m_handlers;
+
+public:
+ //
+ // Event type providers.
+ //
+ ClientEvents& forClient();
+ IStreamEvents& forIStream();
+ IpcClientEvents& forIpcClient();
+ IpcClientProxyEvents& forIpcClientProxy();
+ IpcServerEvents& forIpcServer();
+ IpcServerProxyEvents& forIpcServerProxy();
+ IDataSocketEvents& forIDataSocket();
+ IListenSocketEvents& forIListenSocket();
+ ISocketEvents& forISocket();
+ OSXScreenEvents& forOSXScreen();
+ ClientListenerEvents& forClientListener();
+ ClientProxyEvents& forClientProxy();
+ ClientProxyUnknownEvents& forClientProxyUnknown();
+ ServerEvents& forServer();
+ ServerAppEvents& forServerApp();
+ IKeyStateEvents& forIKeyState();
+ IPrimaryScreenEvents& forIPrimaryScreen();
+ IScreenEvents& forIScreen();
+ ClipboardEvents& forClipboard();
+ FileEvents& forFile();
+
+private:
+ ClientEvents* m_typesForClient;
+ IStreamEvents* m_typesForIStream;
+ IpcClientEvents* m_typesForIpcClient;
+ IpcClientProxyEvents* m_typesForIpcClientProxy;
+ IpcServerEvents* m_typesForIpcServer;
+ IpcServerProxyEvents* m_typesForIpcServerProxy;
+ IDataSocketEvents* m_typesForIDataSocket;
+ IListenSocketEvents* m_typesForIListenSocket;
+ ISocketEvents* m_typesForISocket;
+ OSXScreenEvents* m_typesForOSXScreen;
+ ClientListenerEvents* m_typesForClientListener;
+ ClientProxyEvents* m_typesForClientProxy;
+ ClientProxyUnknownEvents* m_typesForClientProxyUnknown;
+ ServerEvents* m_typesForServer;
+ ServerAppEvents* m_typesForServerApp;
+ IKeyStateEvents* m_typesForIKeyState;
+ IPrimaryScreenEvents* m_typesForIPrimaryScreen;
+ IScreenEvents* m_typesForIScreen;
+ ClipboardEvents* m_typesForClipboard;
+ FileEvents* m_typesForFile;
+ Mutex* m_readyMutex;
+ CondVar<bool>* m_readyCondVar;
+ std::queue<Event> m_pending;
+ NonBlockingStream m_parentStream;
+};
+
+#define EVENT_TYPE_ACCESSOR(type_) \
+type_##Events& \
+EventQueue::for##type_() { \
+ if (m_typesFor##type_ == NULL) { \
+ m_typesFor##type_ = new type_##Events(); \
+ m_typesFor##type_->setEvents(dynamic_cast<IEventQueue*>(this)); \
+ } \
+ return *m_typesFor##type_; \
+}
diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp
new file mode 100644
index 0000000..9a3e46c
--- /dev/null
+++ b/src/lib/base/EventTypes.cpp
@@ -0,0 +1,203 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 Symless Ltd.
+ *
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/EventTypes.h"
+#include "base/IEventQueue.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+EventTypes::EventTypes() :
+ m_events(NULL)
+{
+}
+
+IEventQueue*
+EventTypes::getEvents() const
+{
+ assert(m_events != NULL);
+ return m_events;
+}
+
+void
+EventTypes::setEvents(IEventQueue* events)
+{
+ m_events = events;
+}
+
+//
+// Client
+//
+
+REGISTER_EVENT(Client, connected)
+REGISTER_EVENT(Client, connectionFailed)
+REGISTER_EVENT(Client, disconnected)
+
+//
+// IStream
+//
+
+REGISTER_EVENT(IStream, inputReady)
+REGISTER_EVENT(IStream, outputFlushed)
+REGISTER_EVENT(IStream, outputError)
+REGISTER_EVENT(IStream, inputShutdown)
+REGISTER_EVENT(IStream, outputShutdown)
+
+//
+// IpcClient
+//
+
+REGISTER_EVENT(IpcClient, connected)
+REGISTER_EVENT(IpcClient, messageReceived)
+
+//
+// IpcClientProxy
+//
+
+REGISTER_EVENT(IpcClientProxy, messageReceived)
+REGISTER_EVENT(IpcClientProxy, disconnected)
+
+//
+// IpcServerProxy
+//
+
+REGISTER_EVENT(IpcServerProxy, messageReceived)
+
+//
+// IDataSocket
+//
+
+REGISTER_EVENT(IDataSocket, connected)
+REGISTER_EVENT(IDataSocket, secureConnected)
+REGISTER_EVENT(IDataSocket, connectionFailed)
+
+//
+// IListenSocket
+//
+
+REGISTER_EVENT(IListenSocket, connecting)
+
+//
+// ISocket
+//
+
+REGISTER_EVENT(ISocket, disconnected)
+REGISTER_EVENT(ISocket, stopRetry)
+
+//
+// OSXScreen
+//
+
+REGISTER_EVENT(OSXScreen, confirmSleep)
+
+//
+// ClientListener
+//
+
+REGISTER_EVENT(ClientListener, accepted)
+REGISTER_EVENT(ClientListener, connected)
+
+//
+// ClientProxy
+//
+
+REGISTER_EVENT(ClientProxy, ready)
+REGISTER_EVENT(ClientProxy, disconnected)
+
+//
+// ClientProxyUnknown
+//
+
+REGISTER_EVENT(ClientProxyUnknown, success)
+REGISTER_EVENT(ClientProxyUnknown, failure)
+
+//
+// Server
+//
+
+REGISTER_EVENT(Server, error)
+REGISTER_EVENT(Server, connected)
+REGISTER_EVENT(Server, disconnected)
+REGISTER_EVENT(Server, switchToScreen)
+REGISTER_EVENT(Server, switchInDirection)
+REGISTER_EVENT(Server, keyboardBroadcast)
+REGISTER_EVENT(Server, lockCursorToScreen)
+REGISTER_EVENT(Server, screenSwitched)
+
+//
+// ServerApp
+//
+
+REGISTER_EVENT(ServerApp, reloadConfig)
+REGISTER_EVENT(ServerApp, forceReconnect)
+REGISTER_EVENT(ServerApp, resetServer)
+
+//
+// IKeyState
+//
+
+REGISTER_EVENT(IKeyState, keyDown)
+REGISTER_EVENT(IKeyState, keyUp)
+REGISTER_EVENT(IKeyState, keyRepeat)
+
+//
+// IPrimaryScreen
+//
+
+REGISTER_EVENT(IPrimaryScreen, buttonDown)
+REGISTER_EVENT(IPrimaryScreen, buttonUp)
+REGISTER_EVENT(IPrimaryScreen, motionOnPrimary)
+REGISTER_EVENT(IPrimaryScreen, motionOnSecondary)
+REGISTER_EVENT(IPrimaryScreen, wheel)
+REGISTER_EVENT(IPrimaryScreen, screensaverActivated)
+REGISTER_EVENT(IPrimaryScreen, screensaverDeactivated)
+REGISTER_EVENT(IPrimaryScreen, hotKeyDown)
+REGISTER_EVENT(IPrimaryScreen, hotKeyUp)
+REGISTER_EVENT(IPrimaryScreen, fakeInputBegin)
+REGISTER_EVENT(IPrimaryScreen, fakeInputEnd)
+
+//
+// IScreen
+//
+
+REGISTER_EVENT(IScreen, error)
+REGISTER_EVENT(IScreen, shapeChanged)
+REGISTER_EVENT(IScreen, suspend)
+REGISTER_EVENT(IScreen, resume)
+
+//
+// IpcServer
+//
+
+REGISTER_EVENT(IpcServer, clientConnected)
+REGISTER_EVENT(IpcServer, messageReceived)
+
+//
+// Clipboard
+//
+
+REGISTER_EVENT(Clipboard, clipboardGrabbed)
+REGISTER_EVENT(Clipboard, clipboardChanged)
+REGISTER_EVENT(Clipboard, clipboardSending)
+
+//
+// File
+//
+
+REGISTER_EVENT(File, fileChunkSending)
+REGISTER_EVENT(File, fileRecieveCompleted)
+REGISTER_EVENT(File, keepAlive)
diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h
new file mode 100644
index 0000000..d044c86
--- /dev/null
+++ b/src/lib/base/EventTypes.h
@@ -0,0 +1,754 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-2016 Symless Ltd.
+ *
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/Event.h"
+
+class IEventQueue;
+
+class EventTypes {
+public:
+ EventTypes();
+ void setEvents(IEventQueue* events);
+
+protected:
+ IEventQueue* getEvents() const;
+
+private:
+ IEventQueue* m_events;
+};
+
+#define REGISTER_EVENT(type_, name_) \
+Event::Type \
+type_##Events::name_() \
+{ \
+ return getEvents()->registerTypeOnce(m_##name_, __FUNCTION__); \
+}
+
+class ClientEvents : public EventTypes {
+public:
+ ClientEvents() :
+ m_connected(Event::kUnknown),
+ m_connectionFailed(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent when the client has
+ successfully connected to the server.
+ */
+ Event::Type connected();
+
+ //! Get connection failed event type
+ /*!
+ Returns the connection failed event type. This is sent when the
+ server fails for some reason. The event data is a FailInfo*.
+ */
+ Event::Type connectionFailed();
+
+ //! Get disconnected event type
+ /*!
+ Returns the disconnected event type. This is sent when the client
+ has disconnected from the server (and only after having successfully
+ connected).
+ */
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_connectionFailed;
+ Event::Type m_disconnected;
+};
+
+class IStreamEvents : public EventTypes {
+public:
+ IStreamEvents() :
+ m_inputReady(Event::kUnknown),
+ m_outputFlushed(Event::kUnknown),
+ m_outputError(Event::kUnknown),
+ m_inputShutdown(Event::kUnknown),
+ m_outputShutdown(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get input ready event type
+ /*!
+ Returns the input ready event type. A stream sends this event
+ when \c read() will return with data.
+ */
+ Event::Type inputReady();
+
+ //! Get output flushed event type
+ /*!
+ Returns the output flushed event type. A stream sends this event
+ when the output buffer has been flushed. If there have been no
+ writes since the event was posted, calling \c shutdownOutput() or
+ \c close() will not discard any data and \c flush() will return
+ immediately.
+ */
+ Event::Type outputFlushed();
+
+ //! Get output error event type
+ /*!
+ Returns the output error event type. A stream sends this event
+ when a write has failed.
+ */
+ Event::Type outputError();
+
+ //! Get input shutdown event type
+ /*!
+ Returns the input shutdown event type. This is sent when the
+ input side of the stream has shutdown. When the input has
+ shutdown, no more data will ever be available to read.
+ */
+ Event::Type inputShutdown();
+
+ //! Get output shutdown event type
+ /*!
+ Returns the output shutdown event type. This is sent when the
+ output side of the stream has shutdown. When the output has
+ shutdown, no more data can ever be written to the stream. Any
+ attempt to do so will generate a output error event.
+ */
+ Event::Type outputShutdown();
+
+ //@}
+
+private:
+ Event::Type m_inputReady;
+ Event::Type m_outputFlushed;
+ Event::Type m_outputError;
+ Event::Type m_inputShutdown;
+ Event::Type m_outputShutdown;
+};
+
+class IpcClientEvents : public EventTypes {
+public:
+ IpcClientEvents() :
+ m_connected(Event::kUnknown),
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the socket is connected.
+ Event::Type connected();
+
+ //! Raised when a message is received.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_messageReceived;
+};
+
+class IpcClientProxyEvents : public EventTypes {
+public:
+ IpcClientProxyEvents() :
+ m_messageReceived(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the server receives a message from a client.
+ Event::Type messageReceived();
+
+ //! Raised when the client disconnects from the server.
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_messageReceived;
+ Event::Type m_disconnected;
+};
+
+class IpcServerEvents : public EventTypes {
+public:
+ IpcServerEvents() :
+ m_clientConnected(Event::kUnknown),
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when we have created the client proxy.
+ Event::Type clientConnected();
+
+ //! Raised when a message is received through a client proxy.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_clientConnected;
+ Event::Type m_messageReceived;
+};
+
+class IpcServerProxyEvents : public EventTypes {
+public:
+ IpcServerProxyEvents() :
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the client receives a message from the server.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_messageReceived;
+};
+
+class IDataSocketEvents : public EventTypes {
+public:
+ IDataSocketEvents() :
+ m_connected(Event::kUnknown),
+ m_secureConnected(Event::kUnknown),
+ m_connectionFailed(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connected event type
+ /*!
+ Returns the socket connected event type. A socket sends this
+ event when a remote connection has been established.
+ */
+ Event::Type connected();
+
+ //! Get secure connected event type
+ /*!
+ Returns the secure socket connected event type. A secure socket sends
+ this event when a remote connection has been established.
+ */
+ Event::Type secureConnected();
+
+ //! Get connection failed event type
+ /*!
+ Returns the socket connection failed event type. A socket sends
+ this event when an attempt to connect to a remote port has failed.
+ The data is a pointer to a ConnectionFailedInfo.
+ */
+ Event::Type connectionFailed();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_secureConnected;
+ Event::Type m_connectionFailed;
+};
+
+class IListenSocketEvents : public EventTypes {
+public:
+ IListenSocketEvents() :
+ m_connecting(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connecting event type
+ /*!
+ Returns the socket connecting event type. A socket sends this
+ event when a remote connection is waiting to be accepted.
+ */
+ Event::Type connecting();
+
+ //@}
+
+private:
+ Event::Type m_connecting;
+};
+
+class ISocketEvents : public EventTypes {
+public:
+ ISocketEvents() :
+ m_disconnected(Event::kUnknown),
+ m_stopRetry(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get disconnected event type
+ /*!
+ Returns the socket disconnected event type. A socket sends this
+ event when the remote side of the socket has disconnected or
+ shutdown both input and output.
+ */
+ Event::Type disconnected();
+
+ //! Get stop retry event type
+ /*!
+ Returns the stop retry event type. This is sent when the client
+ doesn't want to reconnect after it disconnects from the server.
+ */
+ Event::Type stopRetry();
+
+ //@}
+
+private:
+ Event::Type m_disconnected;
+ Event::Type m_stopRetry;
+};
+
+class OSXScreenEvents : public EventTypes {
+public:
+ OSXScreenEvents() :
+ m_confirmSleep(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ Event::Type confirmSleep();
+
+ //@}
+
+private:
+ Event::Type m_confirmSleep;
+};
+
+class ClientListenerEvents : public EventTypes {
+public:
+ ClientListenerEvents() :
+ m_accepted(Event::kUnknown),
+ m_connected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get accepted event type
+ /*!
+ Returns the accepted event type. This is sent whenever a server
+ accepts a client.
+ */
+ Event::Type accepted();
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent whenever a
+ a client connects.
+ */
+ Event::Type connected();
+
+ //@}
+
+private:
+ Event::Type m_accepted;
+ Event::Type m_connected;
+};
+
+class ClientProxyEvents : public EventTypes {
+public:
+ ClientProxyEvents() :
+ m_ready(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get ready event type
+ /*!
+ Returns the ready event type. This is sent when the client has
+ completed the initial handshake. Until it is sent, the client is
+ not fully connected.
+ */
+ Event::Type ready();
+
+ //! Get disconnect event type
+ /*!
+ Returns the disconnect event type. This is sent when the client
+ disconnects or is disconnected. The target is getEventTarget().
+ */
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_ready;
+ Event::Type m_disconnected;
+};
+
+class ClientProxyUnknownEvents : public EventTypes {
+public:
+ ClientProxyUnknownEvents() :
+ m_success(Event::kUnknown),
+ m_failure(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get success event type
+ /*!
+ Returns the success event type. This is sent when the client has
+ correctly responded to the hello message. The target is this.
+ */
+ Event::Type success();
+
+ //! Get failure event type
+ /*!
+ Returns the failure event type. This is sent when a client fails
+ to correctly respond to the hello message. The target is this.
+ */
+ Event::Type failure();
+
+ //@}
+
+private:
+ Event::Type m_success;
+ Event::Type m_failure;
+};
+
+class ServerEvents : public EventTypes {
+public:
+ ServerEvents() :
+ m_error(Event::kUnknown),
+ m_connected(Event::kUnknown),
+ m_disconnected(Event::kUnknown),
+ m_switchToScreen(Event::kUnknown),
+ m_switchInDirection(Event::kUnknown),
+ m_keyboardBroadcast(Event::kUnknown),
+ m_lockCursorToScreen(Event::kUnknown),
+ m_screenSwitched(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get error event type
+ /*!
+ Returns the error event type. This is sent when the server fails
+ for some reason.
+ */
+ Event::Type error();
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent when a client screen
+ has connected. The event data is a \c ScreenConnectedInfo* that
+ indicates the connected screen.
+ */
+ Event::Type connected();
+
+ //! Get disconnected event type
+ /*!
+ Returns the disconnected event type. This is sent when all the
+ clients have disconnected.
+ */
+ Event::Type disconnected();
+
+ //! Get switch to screen event type
+ /*!
+ Returns the switch to screen event type. The server responds to this
+ by switching screens. The event data is a \c SwitchToScreenInfo*
+ that indicates the target screen.
+ */
+ Event::Type switchToScreen();
+
+ //! Get switch in direction event type
+ /*!
+ Returns the switch in direction event type. The server responds to this
+ by switching screens. The event data is a \c SwitchInDirectionInfo*
+ that indicates the target direction.
+ */
+ Event::Type switchInDirection();
+
+ //! Get keyboard broadcast event type
+ /*!
+ Returns the keyboard broadcast event type. The server responds
+ to this by turning on keyboard broadcasting or turning it off. The
+ event data is a \c KeyboardBroadcastInfo*.
+ */
+ Event::Type keyboardBroadcast();
+
+ //! Get lock cursor event type
+ /*!
+ Returns the lock cursor event type. The server responds to this
+ by locking the cursor to the active screen or unlocking it. The
+ event data is a \c LockCursorToScreenInfo*.
+ */
+ Event::Type lockCursorToScreen();
+
+ //! Get screen switched event type
+ /*!
+ Returns the screen switched event type. This is raised when the
+ screen has been switched to a client.
+ */
+ Event::Type screenSwitched();
+
+ //@}
+
+private:
+ Event::Type m_error;
+ Event::Type m_connected;
+ Event::Type m_disconnected;
+ Event::Type m_switchToScreen;
+ Event::Type m_switchInDirection;
+ Event::Type m_keyboardBroadcast;
+ Event::Type m_lockCursorToScreen;
+ Event::Type m_screenSwitched;
+};
+
+class ServerAppEvents : public EventTypes {
+public:
+ ServerAppEvents() :
+ m_reloadConfig(Event::kUnknown),
+ m_forceReconnect(Event::kUnknown),
+ m_resetServer(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ Event::Type reloadConfig();
+ Event::Type forceReconnect();
+ Event::Type resetServer();
+
+ //@}
+
+private:
+ Event::Type m_reloadConfig;
+ Event::Type m_forceReconnect;
+ Event::Type m_resetServer;
+};
+
+class IKeyStateEvents : public EventTypes {
+public:
+ IKeyStateEvents() :
+ m_keyDown(Event::kUnknown),
+ m_keyUp(Event::kUnknown),
+ m_keyRepeat(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get key down event type. Event data is KeyInfo*, count == 1.
+ Event::Type keyDown();
+
+ //! Get key up event type. Event data is KeyInfo*, count == 1.
+ Event::Type keyUp();
+
+ //! Get key repeat event type. Event data is KeyInfo*.
+ Event::Type keyRepeat();
+
+ //@}
+
+private:
+ Event::Type m_keyDown;
+ Event::Type m_keyUp;
+ Event::Type m_keyRepeat;
+};
+
+class IPrimaryScreenEvents : public EventTypes {
+public:
+ IPrimaryScreenEvents() :
+ m_buttonDown(Event::kUnknown),
+ m_buttonUp(Event::kUnknown),
+ m_motionOnPrimary(Event::kUnknown),
+ m_motionOnSecondary(Event::kUnknown),
+ m_wheel(Event::kUnknown),
+ m_screensaverActivated(Event::kUnknown),
+ m_screensaverDeactivated(Event::kUnknown),
+ m_hotKeyDown(Event::kUnknown),
+ m_hotKeyUp(Event::kUnknown),
+ m_fakeInputBegin(Event::kUnknown),
+ m_fakeInputEnd(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! button down event type. Event data is ButtonInfo*.
+ Event::Type buttonDown();
+
+ //! button up event type. Event data is ButtonInfo*.
+ Event::Type buttonUp();
+
+ //! mouse motion on the primary screen event type
+ /*!
+ Event data is MotionInfo* and the values are an absolute position.
+ */
+ Event::Type motionOnPrimary();
+
+ //! mouse motion on a secondary screen event type
+ /*!
+ Event data is MotionInfo* and the values are motion deltas not
+ absolute coordinates.
+ */
+ Event::Type motionOnSecondary();
+
+ //! mouse wheel event type. Event data is WheelInfo*.
+ Event::Type wheel();
+
+ //! screensaver activated event type
+ Event::Type screensaverActivated();
+
+ //! screensaver deactivated event type
+ Event::Type screensaverDeactivated();
+
+ //! hot key down event type. Event data is HotKeyInfo*.
+ Event::Type hotKeyDown();
+
+ //! hot key up event type. Event data is HotKeyInfo*.
+ Event::Type hotKeyUp();
+
+ //! start of fake input event type
+ Event::Type fakeInputBegin();
+
+ //! end of fake input event type
+ Event::Type fakeInputEnd();
+
+ //@}
+
+private:
+ Event::Type m_buttonDown;
+ Event::Type m_buttonUp;
+ Event::Type m_motionOnPrimary;
+ Event::Type m_motionOnSecondary;
+ Event::Type m_wheel;
+ Event::Type m_screensaverActivated;
+ Event::Type m_screensaverDeactivated;
+ Event::Type m_hotKeyDown;
+ Event::Type m_hotKeyUp;
+ Event::Type m_fakeInputBegin;
+ Event::Type m_fakeInputEnd;
+};
+
+class IScreenEvents : public EventTypes {
+public:
+ IScreenEvents() :
+ m_error(Event::kUnknown),
+ m_shapeChanged(Event::kUnknown),
+ m_suspend(Event::kUnknown),
+ m_resume(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get error event type
+ /*!
+ Returns the error event type. This is sent whenever the screen has
+ failed for some reason (e.g. the X Windows server died).
+ */
+ Event::Type error();
+
+ //! Get shape changed event type
+ /*!
+ Returns the shape changed event type. This is sent whenever the
+ screen's shape changes.
+ */
+ Event::Type shapeChanged();
+
+ //! Get suspend event type
+ /*!
+ Returns the suspend event type. This is sent whenever the system goes
+ to sleep or a user session is deactivated (fast user switching).
+ */
+ Event::Type suspend();
+
+ //! Get resume event type
+ /*!
+ Returns the resume event type. This is sent whenever the system wakes
+ up or a user session is activated (fast user switching).
+ */
+ Event::Type resume();
+
+ //@}
+
+private:
+ Event::Type m_error;
+ Event::Type m_shapeChanged;
+ Event::Type m_suspend;
+ Event::Type m_resume;
+};
+
+class ClipboardEvents : public EventTypes {
+public:
+ ClipboardEvents() :
+ m_clipboardGrabbed(Event::kUnknown),
+ m_clipboardChanged(Event::kUnknown),
+ m_clipboardSending(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get clipboard grabbed event type
+ /*!
+ Returns the clipboard grabbed event type. This is sent whenever the
+ clipboard is grabbed by some other application so we don't own it
+ anymore. The data is a pointer to a ClipboardInfo.
+ */
+ Event::Type clipboardGrabbed();
+
+ //! Get clipboard changed event type
+ /*!
+ Returns the clipboard changed event type. This is sent whenever the
+ contents of the clipboard has changed. The data is a pointer to a
+ IScreen::ClipboardInfo.
+ */
+ Event::Type clipboardChanged();
+
+ //! Clipboard sending event type
+ /*!
+ Returns the clipboard sending event type. This is used to send
+ clipboard chunks.
+ */
+ Event::Type clipboardSending();
+
+ //@}
+
+private:
+ Event::Type m_clipboardGrabbed;
+ Event::Type m_clipboardChanged;
+ Event::Type m_clipboardSending;
+};
+
+class FileEvents : public EventTypes {
+public:
+ FileEvents() :
+ m_fileChunkSending(Event::kUnknown),
+ m_fileRecieveCompleted(Event::kUnknown),
+ m_keepAlive(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Sending a file chunk
+ Event::Type fileChunkSending();
+
+ //! Completed receiving a file
+ Event::Type fileRecieveCompleted();
+
+ //! Send a keep alive
+ Event::Type keepAlive();
+
+ //@}
+
+private:
+ Event::Type m_fileChunkSending;
+ Event::Type m_fileRecieveCompleted;
+ Event::Type m_keepAlive;
+};
diff --git a/src/lib/base/FunctionEventJob.cpp b/src/lib/base/FunctionEventJob.cpp
new file mode 100644
index 0000000..705e058
--- /dev/null
+++ b/src/lib/base/FunctionEventJob.cpp
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/FunctionEventJob.h"
+
+//
+// FunctionEventJob
+//
+
+FunctionEventJob::FunctionEventJob(
+ void (*func)(const Event&, void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+FunctionEventJob::~FunctionEventJob()
+{
+ // do nothing
+}
+
+void
+FunctionEventJob::run(const Event& event)
+{
+ if (m_func != NULL) {
+ m_func(event, m_arg);
+ }
+}
diff --git a/src/lib/base/FunctionEventJob.h b/src/lib/base/FunctionEventJob.h
new file mode 100644
index 0000000..4b2c2fc
--- /dev/null
+++ b/src/lib/base/FunctionEventJob.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/IEventJob.h"
+
+//! Use a function as an event job
+/*!
+An event job class that invokes a function.
+*/
+class FunctionEventJob : public IEventJob {
+public:
+ //! run() invokes \c func(arg)
+ FunctionEventJob(void (*func)(const Event&, void*), void* arg = NULL);
+ virtual ~FunctionEventJob();
+
+ // IEventJob overrides
+ virtual void run(const Event&);
+
+private:
+ void (*m_func)(const Event&, void*);
+ void* m_arg;
+};
diff --git a/src/lib/base/FunctionJob.cpp b/src/lib/base/FunctionJob.cpp
new file mode 100644
index 0000000..859010e
--- /dev/null
+++ b/src/lib/base/FunctionJob.cpp
@@ -0,0 +1,43 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/FunctionJob.h"
+
+//
+// FunctionJob
+//
+
+FunctionJob::FunctionJob(void (*func)(void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+FunctionJob::~FunctionJob()
+{
+ // do nothing
+}
+
+void
+FunctionJob::run()
+{
+ if (m_func != NULL) {
+ m_func(m_arg);
+ }
+}
diff --git a/src/lib/base/FunctionJob.h b/src/lib/base/FunctionJob.h
new file mode 100644
index 0000000..9cdfa9d
--- /dev/null
+++ b/src/lib/base/FunctionJob.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/IJob.h"
+
+//! Use a function as a job
+/*!
+A job class that invokes a function.
+*/
+class FunctionJob : public IJob {
+public:
+ //! run() invokes \c func(arg)
+ FunctionJob(void (*func)(void*), void* arg = NULL);
+ virtual ~FunctionJob();
+
+ // IJob overrides
+ virtual void run();
+
+private:
+ void (*m_func)(void*);
+ void* m_arg;
+};
diff --git a/src/lib/base/IEventJob.h b/src/lib/base/IEventJob.h
new file mode 100644
index 0000000..3e4a420
--- /dev/null
+++ b/src/lib/base/IEventJob.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/IInterface.h"
+
+class Event;
+
+//! 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 Event&) = 0;
+};
diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h
new file mode 100644
index 0000000..cd4f0b3
--- /dev/null
+++ b/src/lib/base/IEventQueue.h
@@ -0,0 +1,251 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/IInterface.h"
+#include "base/Event.h"
+#include "base/String.h"
+
+class IEventJob;
+class IEventQueueBuffer;
+
+// Opaque type for timer info. This is defined by subclasses of
+// IEventQueueBuffer.
+class EventQueueTimer;
+
+// Event type registration classes.
+class ClientEvents;
+class IStreamEvents;
+class IpcClientEvents;
+class IpcClientProxyEvents;
+class IpcServerEvents;
+class IpcServerProxyEvents;
+class IDataSocketEvents;
+class IListenSocketEvents;
+class ISocketEvents;
+class OSXScreenEvents;
+class ClientListenerEvents;
+class ClientProxyEvents;
+class ClientProxyUnknownEvents;
+class ServerEvents;
+class ServerAppEvents;
+class IKeyStateEvents;
+class IPrimaryScreenEvents;
+class IScreenEvents;
+class ClipboardEvents;
+class FileEvents;
+
+//! Event queue interface
+/*!
+An event queue provides a queue of Events. 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 TimerEvent {
+ public:
+ EventQueueTimer* m_timer; //!< The timer
+ UInt32 m_count; //!< Number of repeats
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Loop the event queue until quit
+ /*!
+ Dequeues and dispatches events until the kQuit event is found.
+ */
+ virtual void loop() = 0;
+
+ //! 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(Event& 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 Event& event) = 0;
+
+ //! Add event to queue
+ /*!
+ Adds \p event to the end of the queue.
+ */
+ virtual void addEvent(const Event& 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 TimerEvent. 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 TimerEvent 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 EventQueueTimer*
+ 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 TimerEvent.
+ The c_count member of the \c TimerEvent 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 EventQueueTimer*
+ 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(EventQueueTimer*) = 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(Event::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(Event::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
+ /*!
+ 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 Event::Type
+ registerTypeOnce(Event::Type& type,
+ const char* name) = 0;
+
+ //! Wait for event queue to become ready
+ /*!
+ Blocks on the current thread until the event queue is ready for events to
+ be added.
+ */
+ virtual void waitForReady() const = 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(Event::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(Event::Type type) = 0;
+
+ //! Get an event type by name
+ /*!
+ Returns the registered type for an event for a given name.
+ */
+ virtual Event::Type getRegisteredType(const String& name) const = 0;
+
+ //! Get the system event type target
+ /*!
+ Returns the target to use for dispatching \c Event::kSystem events.
+ */
+ virtual void* getSystemTarget() = 0;
+
+ //@}
+
+ //
+ // Event type providers.
+ //
+
+ virtual ClientEvents& forClient() = 0;
+ virtual IStreamEvents& forIStream() = 0;
+ virtual IpcClientEvents& forIpcClient() = 0;
+ virtual IpcClientProxyEvents& forIpcClientProxy() = 0;
+ virtual IpcServerEvents& forIpcServer() = 0;
+ virtual IpcServerProxyEvents& forIpcServerProxy() = 0;
+ virtual IDataSocketEvents& forIDataSocket() = 0;
+ virtual IListenSocketEvents& forIListenSocket() = 0;
+ virtual ISocketEvents& forISocket() = 0;
+ virtual OSXScreenEvents& forOSXScreen() = 0;
+ virtual ClientListenerEvents& forClientListener() = 0;
+ virtual ClientProxyEvents& forClientProxy() = 0;
+ virtual ClientProxyUnknownEvents& forClientProxyUnknown() = 0;
+ virtual ServerEvents& forServer() = 0;
+ virtual ServerAppEvents& forServerApp() = 0;
+ virtual IKeyStateEvents& forIKeyState() = 0;
+ virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0;
+ virtual IScreenEvents& forIScreen() = 0;
+ virtual ClipboardEvents& forClipboard() = 0;
+ virtual FileEvents& forFile() = 0;
+};
diff --git a/src/lib/base/IEventQueueBuffer.h b/src/lib/base/IEventQueueBuffer.h
new file mode 100644
index 0000000..b594436
--- /dev/null
+++ b/src/lib/base/IEventQueueBuffer.h
@@ -0,0 +1,101 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/IInterface.h"
+#include "common/basic_types.h"
+
+class Event;
+class EventQueueTimer;
+
+//! 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
+ //@{
+
+ //! Initialize
+ /*!
+ Useful for platform-specific initialisation from a specific thread.
+ */
+ virtual void init() = 0;
+
+ //! 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 Event::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(Event& 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 EventQueueTimer*
+ newTimer(double duration, bool oneShot) const = 0;
+
+ //! Destroy a timer object
+ /*!
+ Destroy a timer object previously returned by \c newTimer().
+ */
+ virtual void deleteTimer(EventQueueTimer*) const = 0;
+
+ //@}
+};
diff --git a/src/lib/base/IJob.h b/src/lib/base/IJob.h
new file mode 100644
index 0000000..f966ec0
--- /dev/null
+++ b/src/lib/base/IJob.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/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;
+};
diff --git a/src/lib/base/ILogOutputter.h b/src/lib/base/ILogOutputter.h
new file mode 100644
index 0000000..ab218fc
--- /dev/null
+++ b/src/lib/base/ILogOutputter.h
@@ -0,0 +1,69 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/Log.h"
+#include "base/ELevel.h"
+#include "common/IInterface.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:
+ //! @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 Log 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;
+
+ //@}
+};
diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp
new file mode 100644
index 0000000..823bf6d
--- /dev/null
+++ b/src/lib/base/Log.cpp
@@ -0,0 +1,309 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "base/log_outputters.h"
+#include "common/Version.h"
+
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <ctime>
+
+// names of priorities
+static const char* g_priority[] = {
+ "FATAL",
+ "ERROR",
+ "WARNING",
+ "NOTE",
+ "INFO",
+ "DEBUG",
+ "DEBUG1",
+ "DEBUG2",
+ "DEBUG3",
+ "DEBUG4",
+ "DEBUG5"
+};
+
+// number of priorities
+static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0]));
+
+// the default priority
+#ifndef NDEBUG
+static const int g_defaultMaxPriority = kDEBUG;
+#else
+static const int g_defaultMaxPriority = kINFO;
+#endif
+
+//
+// Log
+//
+
+Log* Log::s_log = NULL;
+
+Log::Log()
+{
+ 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 ConsoleLogOutputter);
+
+ s_log = this;
+}
+
+Log::Log(Log* src)
+{
+ s_log = src;
+}
+
+Log::~Log()
+{
+ // clean up
+ for (OutputterList::iterator index = m_outputters.begin();
+ index != m_outputters.end(); ++index) {
+ delete *index;
+ }
+ for (OutputterList::iterator index = m_alwaysOutputters.begin();
+ index != m_alwaysOutputters.end(); ++index) {
+ delete *index;
+ }
+ ARCH->closeMutex(m_mutex);
+}
+
+Log*
+Log::getInstance()
+{
+ assert(s_log != NULL);
+ return s_log;
+}
+
+const char*
+Log::getFilterName() const
+{
+ return getFilterName(getFilter());
+}
+
+const char*
+Log::getFilterName(int level) const
+{
+ if (level < 0) {
+ return "Message";
+ }
+ return g_priority[level];
+}
+
+void
+Log::print(const char* file, int line, const char* fmt, ...)
+{
+ // check if fmt begins with a priority argument
+ ELevel priority = kINFO;
+ if ((strlen(fmt) > 2) && (fmt[0] == '%' && fmt[1] == 'z')) {
+
+ // 060 in octal is 0 (48 in decimal), so subtracting this converts ascii
+ // number it a true number. we could use atoi instead, but this is how
+ // it was done originally.
+ priority = (ELevel)(fmt[2] - '\060');
+
+ // move the pointer on past the debug priority char
+ fmt += 3;
+ }
+
+ // done if below priority threshold
+ if (priority > getFilter()) {
+ return;
+ }
+
+ // compute prefix padding length
+ char stack[1024];
+
+ // 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, len - 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.
+ // do not prefix time and file for kPRINT (CLOG_PRINT)
+ if (priority != kPRINT) {
+
+ struct tm *tm;
+ char timestamp[50];
+ time_t t;
+ time(&t);
+ tm = localtime(&t);
+ sprintf(timestamp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ // square brackets, spaces, comma and null terminator take about 10
+ size_t size = 10;
+ size += strlen(timestamp);
+ size += strlen(g_priority[priority]);
+ size += strlen(buffer);
+#ifndef NDEBUG
+ size += strlen(file);
+ // assume there is no file contains over 100k lines of code
+ size += 6;
+#endif
+ char* message = new char[size];
+
+#ifndef NDEBUG
+ sprintf(message, "[%s] %s: %s\n\t%s,%d", timestamp, g_priority[priority], buffer, file, line);
+#else
+ sprintf(message, "[%s] %s: %s", timestamp, g_priority[priority], buffer);
+#endif
+
+ output(priority, message);
+ delete[] message;
+ } else {
+ output(priority, buffer);
+ }
+
+ // clean up
+ if (buffer != stack) {
+ delete[] buffer;
+ }
+}
+
+void
+Log::insert(ILogOutputter* outputter, bool alwaysAtHead)
+{
+ assert(outputter != NULL);
+
+ ArchMutexLock lock(m_mutex);
+ if (alwaysAtHead) {
+ m_alwaysOutputters.push_front(outputter);
+ }
+ else {
+ m_outputters.push_front(outputter);
+ }
+
+ outputter->open(kAppVersion);
+
+ // Issue 41
+ // don't show log unless user requests it, as some users find this
+ // feature irritating (i.e. when they lose network connectivity).
+ // in windows the log window can be displayed by selecting "show log"
+ // from the barrier system tray icon.
+ // if this causes problems for other architectures, then a different
+ // work around should be attempted.
+ //outputter->show(false);
+}
+
+void
+Log::remove(ILogOutputter* outputter)
+{
+ ArchMutexLock lock(m_mutex);
+ m_outputters.remove(outputter);
+ m_alwaysOutputters.remove(outputter);
+}
+
+void
+Log::pop_front(bool alwaysAtHead)
+{
+ ArchMutexLock lock(m_mutex);
+ OutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
+ if (!list->empty()) {
+ delete list->front();
+ list->pop_front();
+ }
+}
+
+bool
+Log::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
+Log::setFilter(int maxPriority)
+{
+ ArchMutexLock lock(m_mutex);
+ m_maxPriority = maxPriority;
+}
+
+int
+Log::getFilter() const
+{
+ ArchMutexLock lock(m_mutex);
+ return m_maxPriority;
+}
+
+void
+Log::output(ELevel priority, char* msg)
+{
+ assert(priority >= -1 && priority < g_numPriority);
+ assert(msg != NULL);
+ if (!msg) return;
+
+ ArchMutexLock lock(m_mutex);
+
+ OutputterList::const_iterator i;
+
+ for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) {
+
+ // write to outputter
+ (*i)->write(priority, msg);
+ }
+
+ for (i = m_outputters.begin(); i != m_outputters.end(); ++i) {
+
+ // write to outputter and break out of loop if it returns false
+ if (!(*i)->write(priority, msg)) {
+ break;
+ }
+ }
+}
diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h
new file mode 100644
index 0000000..1d09be2
--- /dev/null
+++ b/src/lib/base/Log.h
@@ -0,0 +1,211 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "arch/IArchMultithread.h"
+#include "arch/Arch.h"
+#include "common/common.h"
+#include "common/stdlist.h"
+
+#include <stdarg.h>
+
+#define CLOG (Log::getInstance())
+#define BYE "\nTry `%s --help' for more information."
+
+class ILogOutputter;
+class Thread;
+
+//! 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 Log {
+public:
+ Log();
+ Log(Log* src);
+ ~Log();
+
+ //! @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);
+
+ //! Set the minimum priority filter (by ordinal).
+ 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, ...);
+
+ //! Get the minimum priority level.
+ int getFilter() const;
+
+ //! Get the filter name of the current filter level.
+ const char* getFilterName() const;
+
+ //! Get the filter name of a specified filter level.
+ const char* getFilterName(int level) const;
+
+ //! Get the singleton instance of the log
+ static Log* getInstance();
+
+ //! Get the console filter level (messages above this are not sent to console).
+ int getConsoleMaxLevel() const { return kDEBUG2; }
+
+ //@}
+
+private:
+ void output(ELevel priority, char* msg);
+
+private:
+ typedef std::list<ILogOutputter*> OutputterList;
+
+ static Log* s_log;
+
+ ArchMutex m_mutex;
+ OutputterList m_outputters;
+ OutputterList 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 Log::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 Log::print. Otherwise it expands to a call to Log::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 Log::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 Log::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
+
+// the CLOG_* defines are line and file plus %z and an octal number (060=0,
+// 071=9), but the limitation is that once we run out of numbers at either
+// end, then we resort to using non-numerical chars. this still works (since
+// to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10
+
+#define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/'
+#define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0'
+#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"
+#define CLOG_DEBUG3 CLOG_TRACE "%z\070"
+#define CLOG_DEBUG4 CLOG_TRACE "%z\071" // char is '9'
+#define CLOG_DEBUG5 CLOG_TRACE "%z\072" // char is ':'
diff --git a/src/lib/base/NonBlockingStream.cpp b/src/lib/base/NonBlockingStream.cpp
new file mode 100644
index 0000000..d44add1
--- /dev/null
+++ b/src/lib/base/NonBlockingStream.cpp
@@ -0,0 +1,60 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Debauchee Open Source Group
+ *
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined(_WIN32)
+
+#include "base/NonBlockingStream.h"
+
+#include <unistd.h> // tcgetattr/tcsetattr, read
+#include <termios.h> // tcgetattr/tcsetattr
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+NonBlockingStream::NonBlockingStream(int fd) :
+ _fd(fd)
+{
+ // disable ICANON & ECHO so we don't have to wait for a newline
+ // before we get data (and to keep it from being echoed back out)
+ termios ta;
+ tcgetattr(fd, &ta);
+ _p_ta_previous = new termios(ta);
+ ta.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(fd, TCSANOW, &ta);
+
+ // prevent IO from blocking so we can poll (read())
+ int _cntl_previous = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, _cntl_previous | O_NONBLOCK);
+}
+
+NonBlockingStream::~NonBlockingStream()
+{
+ tcsetattr(_fd, TCSANOW, _p_ta_previous);
+ fcntl(_fd, F_SETFL, _cntl_previous);
+ delete _p_ta_previous;
+}
+
+bool NonBlockingStream::try_read_char(char &ch) const
+{
+ int result = read(_fd, &ch, 1);
+ if (result == 1)
+ return true;
+ assert(result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
+ return false;
+}
+
+#endif // !defined(_WIN32)
diff --git a/src/lib/base/NonBlockingStream.h b/src/lib/base/NonBlockingStream.h
new file mode 100644
index 0000000..4c27762
--- /dev/null
+++ b/src/lib/base/NonBlockingStream.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Debauchee Open Source Group
+ *
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+// windows doesn't have a unistd.h so this class won't work as-written.
+// at the moment barrier doesn't need this functionality on windows so
+// it's left as a stub to be optimized out
+#if defined(_WIN32)
+
+class NonBlockingStream
+{
+public:
+ bool try_read_char(char &ch) const { return false; };
+};
+
+#else // non-windows platforms
+
+struct termios;
+
+class NonBlockingStream
+{
+public:
+ explicit NonBlockingStream(int fd = 0);
+ ~NonBlockingStream();
+
+ bool try_read_char(char &ch) const;
+
+private:
+ int _fd;
+ termios * _p_ta_previous;
+ int _cntl_previous;
+};
+
+#endif
diff --git a/src/lib/base/PriorityQueue.h b/src/lib/base/PriorityQueue.h
new file mode 100644
index 0000000..d2ca70e
--- /dev/null
+++ b/src/lib/base/PriorityQueue.h
@@ -0,0 +1,138 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/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 PriorityQueue {
+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;
+
+ PriorityQueue() { }
+ PriorityQueue(Container& swappedIn) { swap(swappedIn); }
+ ~PriorityQueue() { }
+
+ //! @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(PriorityQueue<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;
+};
diff --git a/src/lib/base/SimpleEventQueueBuffer.cpp b/src/lib/base/SimpleEventQueueBuffer.cpp
new file mode 100644
index 0000000..b55fe55
--- /dev/null
+++ b/src/lib/base/SimpleEventQueueBuffer.cpp
@@ -0,0 +1,101 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/SimpleEventQueueBuffer.h"
+#include "base/Stopwatch.h"
+#include "arch/Arch.h"
+
+class EventQueueTimer { };
+
+//
+// SimpleEventQueueBuffer
+//
+
+SimpleEventQueueBuffer::SimpleEventQueueBuffer()
+{
+ m_queueMutex = ARCH->newMutex();
+ m_queueReadyCond = ARCH->newCondVar();
+ m_queueReady = false;
+}
+
+SimpleEventQueueBuffer::~SimpleEventQueueBuffer()
+{
+ ARCH->closeCondVar(m_queueReadyCond);
+ ARCH->closeMutex(m_queueMutex);
+}
+
+void
+SimpleEventQueueBuffer::waitForEvent(double timeout)
+{
+ ArchMutexLock lock(m_queueMutex);
+ Stopwatch 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
+SimpleEventQueueBuffer::getEvent(Event&, UInt32& dataID)
+{
+ ArchMutexLock lock(m_queueMutex);
+ if (!m_queueReady) {
+ return kNone;
+ }
+ dataID = m_queue.back();
+ m_queue.pop_back();
+ m_queueReady = !m_queue.empty();
+ return kUser;
+}
+
+bool
+SimpleEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ ArchMutexLock lock(m_queueMutex);
+ m_queue.push_front(dataID);
+ if (!m_queueReady) {
+ m_queueReady = true;
+ ARCH->broadcastCondVar(m_queueReadyCond);
+ }
+ return true;
+}
+
+bool
+SimpleEventQueueBuffer::isEmpty() const
+{
+ ArchMutexLock lock(m_queueMutex);
+ return !m_queueReady;
+}
+
+EventQueueTimer*
+SimpleEventQueueBuffer::newTimer(double, bool) const
+{
+ return new EventQueueTimer;
+}
+
+void
+SimpleEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
+{
+ delete timer;
+}
diff --git a/src/lib/base/SimpleEventQueueBuffer.h b/src/lib/base/SimpleEventQueueBuffer.h
new file mode 100644
index 0000000..4aa76d3
--- /dev/null
+++ b/src/lib/base/SimpleEventQueueBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/IEventQueueBuffer.h"
+#include "arch/IArchMultithread.h"
+#include "common/stddeque.h"
+
+//! In-memory event queue buffer
+/*!
+An event queue buffer provides a queue of events for an IEventQueue.
+*/
+class SimpleEventQueueBuffer : public IEventQueueBuffer {
+public:
+ SimpleEventQueueBuffer();
+ ~SimpleEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ void init() { }
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(Event& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(EventQueueTimer*) const;
+
+private:
+ typedef std::deque<UInt32> EventDeque;
+
+ ArchMutex m_queueMutex;
+ ArchCond m_queueReadyCond;
+ bool m_queueReady;
+ EventDeque m_queue;
+};
+
diff --git a/src/lib/base/Stopwatch.cpp b/src/lib/base/Stopwatch.cpp
new file mode 100644
index 0000000..b9ceb85
--- /dev/null
+++ b/src/lib/base/Stopwatch.cpp
@@ -0,0 +1,130 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/Stopwatch.h"
+#include "arch/Arch.h"
+
+//
+// Stopwatch
+//
+
+Stopwatch::Stopwatch(bool triggered) :
+ m_mark(0.0),
+ m_triggered(triggered),
+ m_stopped(triggered)
+{
+ if (!triggered) {
+ m_mark = ARCH->time();
+ }
+}
+
+Stopwatch::~Stopwatch()
+{
+ // do nothing
+}
+
+double
+Stopwatch::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
+Stopwatch::stop()
+{
+ if (m_stopped) {
+ return;
+ }
+
+ // save the elapsed time
+ m_mark = ARCH->time() - m_mark;
+ m_stopped = true;
+}
+
+void
+Stopwatch::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
+Stopwatch::setTrigger()
+{
+ stop();
+ m_triggered = true;
+}
+
+double
+Stopwatch::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;
+ }
+}
+
+Stopwatch::operator double()
+{
+ return getTime();
+}
+
+bool
+Stopwatch::isStopped() const
+{
+ return m_stopped;
+}
+
+double
+Stopwatch::getTime() const
+{
+ if (m_stopped) {
+ return m_mark;
+ }
+ else {
+ return ARCH->time() - m_mark;
+ }
+}
+
+Stopwatch::operator double() const
+{
+ return getTime();
+}
diff --git a/src/lib/base/Stopwatch.h b/src/lib/base/Stopwatch.h
new file mode 100644
index 0000000..dda74ea
--- /dev/null
+++ b/src/lib/base/Stopwatch.h
@@ -0,0 +1,109 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/common.h"
+
+//! A timer class
+/*!
+This class measures time intervals. All time interval measurement
+should use this class.
+*/
+class Stopwatch {
+public:
+ /*!
+ The default constructor does an implicit reset() or setTrigger().
+ If triggered == false then the clock starts ticking.
+ */
+ Stopwatch(bool triggered = false);
+ ~Stopwatch();
+
+ //! @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;
+};
diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp
new file mode 100644
index 0000000..97b8997
--- /dev/null
+++ b/src/lib/base/String.cpp
@@ -0,0 +1,295 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ *
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "arch/Arch.h"
+#include "base/String.h"
+#include "common/common.h"
+#include "common/stdvector.h"
+
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <stdio.h>
+#include <cstdarg>
+#include <sstream>
+#include <iomanip>
+#include <algorithm>
+#include <cerrno>
+
+namespace barrier {
+namespace string {
+
+String
+format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ String result = vformat(fmt, args);
+ va_end(args);
+ return result;
+}
+
+String
+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<size_t> index;
+ size_t 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<size_t>((scan - 1) - fmt));
+ width.push_back(2);
+ }
+ else if (*scan == '{') {
+ // get argument index
+ char* end;
+ errno = 0;
+ long i = strtol(scan + 1, &end, 10);
+ if (errno || (i < 0) || (*end != '}')) {
+ // invalid index -- ignore
+ scan = end - 1; // BUG if there are digits?
+ }
+ else {
+ index.push_back(i);
+ pos.push_back(static_cast<size_t>((scan - 1) - fmt));
+ width.push_back(static_cast<size_t>((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
+ String 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;
+}
+
+String
+sprintf(const char* fmt, ...)
+{
+ char tmp[1024];
+ char* buffer = tmp;
+ int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
+ String 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;
+}
+
+void
+findReplaceAll(
+ String& subject,
+ const String& find,
+ const String& replace)
+{
+ size_t pos = 0;
+ while ((pos = subject.find(find, pos)) != String::npos) {
+ subject.replace(pos, find.length(), replace);
+ pos += replace.length();
+ }
+}
+
+String
+removeFileExt(String filename)
+{
+ size_t dot = filename.find_last_of('.');
+
+ if (dot == String::npos) {
+ return filename;
+ }
+
+ return filename.substr(0, dot);
+}
+
+void
+toHex(String& subject, int width, const char fill)
+{
+ std::stringstream ss;
+ ss << std::hex;
+ for (unsigned int i = 0; i < subject.length(); i++) {
+ ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i];
+ }
+
+ subject = ss.str();
+}
+
+void
+uppercase(String& subject)
+{
+ std::transform(subject.begin(), subject.end(), subject.begin(), ::toupper);
+}
+
+void
+removeChar(String& subject, const char c)
+{
+ subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
+}
+
+String
+sizeTypeToString(size_t n)
+{
+ std::stringstream ss;
+ ss << n;
+ return ss.str();
+}
+
+size_t
+stringToSizeType(String string)
+{
+ std::istringstream iss(string);
+ size_t value;
+ iss >> value;
+ return value;
+}
+
+std::vector<String>
+splitString(String string, const char c)
+{
+ std::vector<String> results;
+
+ size_t head = 0;
+ size_t separator = string.find(c);
+ while (separator != String::npos) {
+ if (head!=separator) {
+ results.push_back(string.substr(head, separator - head));
+ }
+ head = separator + 1;
+ separator = string.find(c, head);
+ }
+
+ if (head < string.size()) {
+ results.push_back(string.substr(head, string.size() - head));
+ }
+
+ return results;
+}
+
+//
+// CaselessCmp
+//
+
+bool
+CaselessCmp::cmpEqual(
+ const String::value_type& a,
+ const String::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) == tolower(b);
+}
+
+bool
+CaselessCmp::cmpLess(
+ const String::value_type& a,
+ const String::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) < tolower(b);
+}
+
+bool
+CaselessCmp::less(const String& a, const String& b)
+{
+ return std::lexicographical_compare(
+ a.begin(), a.end(),
+ b.begin(), b.end(),
+ &barrier::string::CaselessCmp::cmpLess);
+}
+
+bool
+CaselessCmp::equal(const String& a, const String& b)
+{
+ return !(less(a, b) || less(b, a));
+}
+
+bool
+CaselessCmp::operator()(const String& a, const String& b) const
+{
+ return less(a, b);
+}
+
+}
+}
diff --git a/src/lib/base/String.h b/src/lib/base/String.h
new file mode 100644
index 0000000..3661461
--- /dev/null
+++ b/src/lib/base/String.h
@@ -0,0 +1,135 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/common.h"
+#include "common/stdstring.h"
+
+#include <stdarg.h>
+#include <vector>
+
+// use standard C++ string class for our string class
+typedef std::string String;
+
+namespace barrier {
+
+//! String utilities
+/*!
+Provides functions for string manipulation.
+*/
+namespace string {
+
+//! Format positional arguments
+/*!
+Format a string using positional arguments. fmt has literal
+characters and conversion specifications introduced by `\%':
+- \%\% -- literal `\%'
+- \%{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.
+*/
+String format(const char* fmt, ...);
+
+//! Format positional arguments
+/*!
+Same as format() except takes va_list.
+*/
+String vformat(const char* fmt, va_list);
+
+//! Print a string using sprintf-style formatting
+/*!
+Equivalent to sprintf() except the result is returned as a String.
+*/
+String sprintf(const char* fmt, ...);
+
+//! Find and replace all
+/*!
+Finds \c find inside \c subject and replaces it with \c replace
+*/
+void findReplaceAll(String& subject, const String& find, const String& replace);
+
+//! Remove file extension
+/*!
+Finds the last dot and remove all characters from the dot to the end
+*/
+String removeFileExt(String filename);
+
+//! Convert into hexdecimal
+/*!
+Convert each character in \c subject into hexdecimal form with \c width
+*/
+void toHex(String& subject, int width, const char fill = '0');
+
+//! Convert to all uppercase
+/*!
+Convert each character in \c subject to uppercase
+*/
+void uppercase(String& subject);
+
+//! Remove all specific char in suject
+/*!
+Remove all specific \c c in \c suject
+*/
+void removeChar(String& subject, const char c);
+
+//! Convert a size type to a string
+/*!
+Convert an size type to a string
+*/
+String sizeTypeToString(size_t n);
+
+//! Convert a string to a size type
+/*!
+Convert an a \c string to an size type
+*/
+size_t stringToSizeType(String string);
+
+//! Split a string into substrings
+/*!
+Split a \c string that separated by a \c c into substrings
+*/
+std::vector<String> splitString(String string, const char c);
+
+//! Case-insensitive comparisons
+/*!
+This class provides case-insensitve comparison functions.
+*/
+class CaselessCmp {
+ public:
+ //! Same as less()
+ bool operator()(const String& a, const String& b) const;
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool less(const String& a, const String& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool equal(const String& a, const String& b);
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool cmpLess(const String::value_type& a,
+ const String::value_type& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool cmpEqual(const String::value_type& a,
+ const String::value_type& b);
+};
+
+}
+}
diff --git a/src/lib/base/TMethodEventJob.h b/src/lib/base/TMethodEventJob.h
new file mode 100644
index 0000000..a65f8c9
--- /dev/null
+++ b/src/lib/base/TMethodEventJob.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#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 Event&, void*),
+ void* arg = NULL);
+ virtual ~TMethodEventJob();
+
+ // IJob overrides
+ virtual void run(const Event&);
+
+private:
+ T* m_object;
+ void (T::*m_method)(const Event&, void*);
+ void* m_arg;
+};
+
+template <class T>
+inline
+TMethodEventJob<T>::TMethodEventJob(T* object,
+ void (T::*method)(const Event&, 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 Event& event)
+{
+ if (m_object != NULL) {
+ (m_object->*m_method)(event, m_arg);
+ }
+}
diff --git a/src/lib/base/TMethodJob.h b/src/lib/base/TMethodJob.h
new file mode 100644
index 0000000..ec88f05
--- /dev/null
+++ b/src/lib/base/TMethodJob.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#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);
+ }
+}
diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp
new file mode 100644
index 0000000..6a077e7
--- /dev/null
+++ b/src/lib/base/Unicode.cpp
@@ -0,0 +1,784 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "arch/Arch.h"
+#include "base/Unicode.h"
+
+#include <cstring>
+
+//
+// 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;
+ }
+}
+
+
+//
+// Unicode
+//
+
+UInt32 Unicode::s_invalid = 0x0000ffff;
+UInt32 Unicode::s_replacement = 0x0000fffd;
+
+bool
+Unicode::isUTF8(const String& src)
+{
+ // convert and test each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ for (UInt32 n = (UInt32)src.size(); n > 0; ) {
+ if (fromUTF8(data, n) == s_invalid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+String
+Unicode::UTF8ToUCS2(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String 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;
+}
+
+String
+Unicode::UTF8ToUCS4(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String 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;
+}
+
+String
+Unicode::UTF8ToUTF16(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String 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;
+}
+
+String
+Unicode::UTF8ToUTF32(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String 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;
+}
+
+String
+Unicode::UTF8ToText(const String& 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);
+ String text(mbs, len);
+
+ // clean up
+ delete[] mbs;
+ delete[] tmp;
+
+ return text;
+}
+
+String
+Unicode::UCS2ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 1;
+ return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UCS4ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 2;
+ return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UTF16ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 1;
+ return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UTF32ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 2;
+ return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::textToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert string to wide characters
+ UInt32 n = (UInt32)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
+ String utf8 = wideCharToUTF8(wcs, len, errors);
+
+ // clean up
+ delete[] wcs;
+
+ return utf8;
+}
+
+wchar_t*
+Unicode::UTF8ToWideChar(const String& src, UInt32& size, bool* errors)
+{
+ // convert to platform's wide character encoding
+ String tmp;
+ switch (ARCH->getWideCharEncoding()) {
+ case IArchString::kUCS2:
+ tmp = UTF8ToUCS2(src, errors);
+ size = (UInt32)tmp.size() >> 1;
+ break;
+
+ case IArchString::kUCS4:
+ tmp = UTF8ToUCS4(src, errors);
+ size = (UInt32)tmp.size() >> 2;
+ break;
+
+ case IArchString::kUTF16:
+ tmp = UTF8ToUTF16(src, errors);
+ size = (UInt32)tmp.size() >> 1;
+ break;
+
+ case IArchString::kUTF32:
+ tmp = UTF8ToUTF32(src, errors);
+ size = (UInt32)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;
+}
+
+String
+Unicode::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 String'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 String();
+ }
+}
+
+String
+Unicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String 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;
+}
+
+String
+Unicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String 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;
+}
+
+String
+Unicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String 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;
+}
+
+String
+Unicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String 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
+Unicode::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
+Unicode::toUTF8(String& 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");
+ }
+}
diff --git a/src/lib/base/Unicode.h b/src/lib/base/Unicode.h
new file mode 100644
index 0000000..1391c1e
--- /dev/null
+++ b/src/lib/base/Unicode.h
@@ -0,0 +1,144 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/String.h"
+#include "common/basic_types.h"
+
+//! Unicode utility functions
+/*!
+This class provides functions for converting between various Unicode
+encodings and the current locale encoding.
+*/
+class Unicode {
+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 String&);
+
+ //! 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 String UTF8ToUCS2(const String&, 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 String UTF8ToUCS4(const String&, 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 String UTF8ToUTF16(const String&, 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 String UTF8ToUTF32(const String&, 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 String UTF8ToText(const String&, 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 String UCS2ToUTF8(const String&, 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 String UCS4ToUTF8(const String&, 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 String UTF16ToUTF8(const String&, 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 String UTF32ToUTF8(const String&, 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 String textToUTF8(const String&, 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 String&,
+ UInt32& size, bool* errors);
+
+ // convert nul terminated wchar_t string (in platform's native
+ // encoding) to UTF8.
+ static String wideCharToUTF8(const wchar_t*,
+ UInt32 size, bool* errors);
+
+ // internal conversion to UTF8
+ static String doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+
+ // convert characters to/from UTF8
+ static UInt32 fromUTF8(const UInt8*& src, UInt32& size);
+ static void toUTF8(String& dst, UInt32 c, bool* errors);
+
+private:
+ static UInt32 s_invalid;
+ static UInt32 s_replacement;
+};
diff --git a/src/lib/base/XBase.cpp b/src/lib/base/XBase.cpp
new file mode 100644
index 0000000..29ae927
--- /dev/null
+++ b/src/lib/base/XBase.cpp
@@ -0,0 +1,76 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/XBase.h"
+#include "base/String.h"
+
+#include <cerrno>
+#include <cstdarg>
+
+//
+// XBase
+//
+
+XBase::XBase() :
+ std::runtime_error("")
+{
+ // do nothing
+}
+
+XBase::XBase(const String& msg) :
+ std::runtime_error(msg)
+{
+ // do nothing
+}
+
+XBase::~XBase() _NOEXCEPT
+{
+ // do nothing
+}
+
+const char*
+XBase::what() const _NOEXCEPT
+{
+ const char* what = std::runtime_error::what();
+ if (strlen(what) == 0) {
+ m_what = getWhat();
+ return m_what.c_str();
+ }
+ return what;
+}
+
+String
+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
+ String result;
+ va_list args;
+ va_start(args, fmt);
+ try {
+ result = barrier::string::vformat(fmt, args);
+ }
+ catch (...) {
+ // ignore
+ }
+ va_end(args);
+
+ return result;
+}
diff --git a/src/lib/base/XBase.h b/src/lib/base/XBase.h
new file mode 100644
index 0000000..3064b6c
--- /dev/null
+++ b/src/lib/base/XBase.h
@@ -0,0 +1,125 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "base/String.h"
+#include "common/stdexcept.h"
+
+//! Exception base class
+/*!
+This is the base class of most exception types.
+*/
+class XBase : public std::runtime_error {
+public:
+ //! Use getWhat() as the result of what()
+ XBase();
+ //! Use \c msg as the result of what()
+ XBase(const String& msg);
+ virtual ~XBase() _NOEXCEPT;
+
+ //! Reason for exception
+ virtual const char* what() const _NOEXCEPT;
+
+protected:
+ //! Get a human readable string describing the exception
+ virtual String getWhat() const throw() { return ""; }
+
+ //! 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 String format(const char* id,
+ const char* defaultFormat, ...) const throw();
+private:
+ mutable String m_what;
+};
+
+/*!
+\def XBASE_SUBCLASS
+Convenience macro to subclass from XBase (or a subclass of it),
+providing the c'tor taking a const String&. getWhat() is not
+declared.
+*/
+#define XBASE_SUBCLASS(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_() : super_() { } \
+ name_(const String& msg) : super_(msg) { } \
+ virtual ~name_() _NOEXCEPT { } \
+}
+
+/*!
+\def XBASE_SUBCLASS
+Convenience macro to subclass from XBase (or a subclass of it),
+providing the c'tor taking a const String&. getWhat() must be
+implemented.
+*/
+#define XBASE_SUBCLASS_WHAT(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_() : super_() { } \
+ name_(const String& msg) : super_(msg) { } \
+ virtual ~name_() _NOEXCEPT { } \
+ \
+protected: \
+ virtual String 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 String&. 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 String& msg) : super_(msg), m_state(kFirst) { } \
+ virtual ~name_() _NOEXCEPT { } \
+ \
+ virtual const char* what() const _NOEXCEPT \
+ { \
+ 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 String getWhat() const throw(); \
+ \
+private: \
+ mutable EState m_state; \
+ mutable std::string m_formatted; \
+}
diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp
new file mode 100644
index 0000000..8e56c26
--- /dev/null
+++ b/src/lib/base/log_outputters.cpp
@@ -0,0 +1,337 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "base/log_outputters.h"
+#include "base/TMethodJob.h"
+#include "arch/Arch.h"
+
+#include <fstream>
+
+enum EFileLogOutputter {
+ kFileSizeLimit = 1024 // kb
+};
+
+//
+// StopLogOutputter
+//
+
+StopLogOutputter::StopLogOutputter()
+{
+ // do nothing
+}
+
+StopLogOutputter::~StopLogOutputter()
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::open(const char*)
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::close()
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::show(bool)
+{
+ // do nothing
+}
+
+bool
+StopLogOutputter::write(ELevel, const char*)
+{
+ return false;
+}
+
+
+//
+// ConsoleLogOutputter
+//
+
+ConsoleLogOutputter::ConsoleLogOutputter()
+{
+}
+
+ConsoleLogOutputter::~ConsoleLogOutputter()
+{
+}
+
+void
+ConsoleLogOutputter::open(const char* title)
+{
+ ARCH->openConsole(title);
+}
+
+void
+ConsoleLogOutputter::close()
+{
+ ARCH->closeConsole();
+}
+
+void
+ConsoleLogOutputter::show(bool showIfEmpty)
+{
+ ARCH->showConsole(showIfEmpty);
+}
+
+bool
+ConsoleLogOutputter::write(ELevel level, const char* msg)
+{
+ ARCH->writeConsole(level, msg);
+ return true;
+}
+
+void
+ConsoleLogOutputter::flush()
+{
+
+}
+
+
+//
+// SystemLogOutputter
+//
+
+SystemLogOutputter::SystemLogOutputter()
+{
+ // do nothing
+}
+
+SystemLogOutputter::~SystemLogOutputter()
+{
+ // do nothing
+}
+
+void
+SystemLogOutputter::open(const char* title)
+{
+ ARCH->openLog(title);
+}
+
+void
+SystemLogOutputter::close()
+{
+ ARCH->closeLog();
+}
+
+void
+SystemLogOutputter::show(bool showIfEmpty)
+{
+ ARCH->showLog(showIfEmpty);
+}
+
+bool
+SystemLogOutputter::write(ELevel level, const char* msg)
+{
+ ARCH->writeLog(level, msg);
+ return true;
+}
+
+//
+// SystemLogger
+//
+
+SystemLogger::SystemLogger(const char* title, bool blockConsole) :
+ m_stop(NULL)
+{
+ // redirect log messages
+ if (blockConsole) {
+ m_stop = new StopLogOutputter;
+ CLOG->insert(m_stop);
+ }
+ m_syslog = new SystemLogOutputter;
+ m_syslog->open(title);
+ CLOG->insert(m_syslog);
+}
+
+SystemLogger::~SystemLogger()
+{
+ CLOG->remove(m_syslog);
+ delete m_syslog;
+ if (m_stop != NULL) {
+ CLOG->remove(m_stop);
+ delete m_stop;
+ }
+}
+
+
+//
+// BufferedLogOutputter
+//
+
+BufferedLogOutputter::BufferedLogOutputter(UInt32 maxBufferSize) :
+ m_maxBufferSize(maxBufferSize)
+{
+ // do nothing
+}
+
+BufferedLogOutputter::~BufferedLogOutputter()
+{
+ // do nothing
+}
+
+BufferedLogOutputter::const_iterator
+BufferedLogOutputter::begin() const
+{
+ return m_buffer.begin();
+}
+
+BufferedLogOutputter::const_iterator
+BufferedLogOutputter::end() const
+{
+ return m_buffer.end();
+}
+
+void
+BufferedLogOutputter::open(const char*)
+{
+ // do nothing
+}
+
+void
+BufferedLogOutputter::close()
+{
+ // remove all elements from the buffer
+ m_buffer.clear();
+}
+
+void
+BufferedLogOutputter::show(bool)
+{
+ // do nothing
+}
+
+bool
+BufferedLogOutputter::write(ELevel, const char* message)
+{
+ while (m_buffer.size() >= m_maxBufferSize) {
+ m_buffer.pop_front();
+ }
+ m_buffer.push_back(String(message));
+ return true;
+}
+
+
+//
+// FileLogOutputter
+//
+
+FileLogOutputter::FileLogOutputter(const char* logFile)
+{
+ setLogFilename(logFile);
+}
+
+FileLogOutputter::~FileLogOutputter()
+{
+}
+
+void
+FileLogOutputter::setLogFilename(const char* logFile)
+{
+ assert(logFile != NULL);
+ m_fileName = logFile;
+}
+
+bool
+FileLogOutputter::write(ELevel level, const char *message)
+{
+ bool moveFile = false;
+
+ std::ofstream m_handle;
+ m_handle.open(m_fileName.c_str(), std::fstream::app);
+ if (m_handle.is_open() && m_handle.fail() != true) {
+ m_handle << message << std::endl;
+
+ // when file size exceeds limits, move to 'old log' filename.
+ size_t p = m_handle.tellp();
+ if (p > (kFileSizeLimit * 1024)) {
+ moveFile = true;
+ }
+ }
+ m_handle.close();
+
+ if (moveFile) {
+ String oldLogFilename = barrier::string::sprintf("%s.1", m_fileName.c_str());
+ remove(oldLogFilename.c_str());
+ rename(m_fileName.c_str(), oldLogFilename.c_str());
+ }
+
+ return true;
+}
+
+void
+FileLogOutputter::open(const char *title) {}
+
+void
+FileLogOutputter::close() {}
+
+void
+FileLogOutputter::show(bool showIfEmpty) {}
+
+//
+// MesssageBoxLogOutputter
+//
+
+MesssageBoxLogOutputter::MesssageBoxLogOutputter()
+{
+ // do nothing
+}
+
+MesssageBoxLogOutputter::~MesssageBoxLogOutputter()
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::open(const char* title)
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::close()
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::show(bool showIfEmpty)
+{
+ // do nothing
+}
+
+bool
+MesssageBoxLogOutputter::write(ELevel level, const char* msg)
+{
+ // don't spam user with messages.
+ if (level > kERROR) {
+ return true;
+ }
+
+#if SYSAPI_WIN32
+ MessageBox(NULL, msg, CLOG->getFilterName(level), MB_OK);
+#endif
+
+ return true;
+}
diff --git a/src/lib/base/log_outputters.h b/src/lib/base/log_outputters.h
new file mode 100644
index 0000000..c4940aa
--- /dev/null
+++ b/src/lib/base/log_outputters.h
@@ -0,0 +1,172 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * 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 LICENSE 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mt/Thread.h"
+#include "base/ILogOutputter.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+#include "common/stddeque.h"
+
+#include <list>
+#include <fstream>
+
+//! 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 StopLogOutputter : public ILogOutputter {
+public:
+ StopLogOutputter();
+ virtual ~StopLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};
+
+//! Write log to console
+/*!
+This outputter writes output to the console. The level for each
+message is ignored.
+*/
+class ConsoleLogOutputter : public ILogOutputter {
+public:
+ ConsoleLogOutputter();
+ virtual ~ConsoleLogOutputter();
+
+ // 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 void flush();
+};
+
+//! Write log to file
+/*!
+This outputter writes output to the file. The level for each
+message is ignored.
+*/
+
+class FileLogOutputter : public ILogOutputter {
+public:
+ FileLogOutputter(const char* logFile);
+ virtual ~FileLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+
+ void setLogFilename(const char* title);
+
+private:
+ std::string m_fileName;
+};
+
+//! Write log to system log
+/*!
+This outputter writes output to the system log.
+*/
+class SystemLogOutputter : public ILogOutputter {
+public:
+ SystemLogOutputter();
+ virtual ~SystemLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};
+
+//! Write log to system log only
+/*!
+Creating an object of this type inserts a StopLogOutputter followed
+by a SystemLogOutputter into Log. 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 SystemLogger {
+public:
+ SystemLogger(const char* title, bool blockConsole);
+ ~SystemLogger();
+
+private:
+ ILogOutputter* m_syslog;
+ ILogOutputter* m_stop;
+};
+
+//! Save log history
+/*!
+This outputter records the last N log messages.
+*/
+class BufferedLogOutputter : public ILogOutputter {
+private:
+ typedef std::deque<String> Buffer;
+
+public:
+ typedef Buffer::const_iterator const_iterator;
+
+ BufferedLogOutputter(UInt32 maxBufferSize);
+ virtual ~BufferedLogOutputter();
+
+ //! @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);
+private:
+ UInt32 m_maxBufferSize;
+ Buffer m_buffer;
+};
+
+//! Write log to message box
+/*!
+The level for each message is ignored.
+*/
+class MesssageBoxLogOutputter : public ILogOutputter {
+public:
+ MesssageBoxLogOutputter();
+ virtual ~MesssageBoxLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};