/* * 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 . */ #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(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(m_readyMutex, false)) { 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); } 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) { std::lock_guard lock(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 ""; } else { return i->second; } } } void EventQueue::adoptBuffer(IEventQueueBuffer* buffer) { std::lock_guard lock(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: { std::lock_guard lock(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) { std::lock_guard lock(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; } std::lock_guard lock(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; } std::lock_guard lock(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) { std::lock_guard lock(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) { std::lock_guard lock(mutex_); IEventJob*& job = m_handlers[target][type]; delete job; job = handler; } void EventQueue::removeHandler(Event::Type type, void* target) { IEventJob* handler = NULL; { std::lock_guard lock(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 handlers; { std::lock_guard lock(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::iterator index = handlers.begin(); index != handlers.end(); ++index) { delete *index; } } IEventJob* EventQueue::getHandler(Event::Type type, void* target) const { std::lock_guard lock(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(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 std::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((m_timeout - m_time) / m_timeout); } } bool EventQueue::Timer::operator<(const Timer& t) const { return m_time < t.m_time; }