// SPDX-License-Identifier: BSD-2-Clause // This code is part of the sfizz library and is licensed under a BSD 2-clause // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #if !defined(__APPLE__) && !defined(_WIN32) #include "X11RunLoop.h" #include "vstgui/lib/platform/linux/x11platform.h" #include "base/source/fobject.h" #include #include #include #include namespace VSTGUI { struct RunLoop::Impl { struct EventHandler; struct TimerHandler; using EventHandlers = std::vector>; using TimerHandlers = std::vector>; EventHandlers eventHandlers; TimerHandlers timerHandlers; Steinberg::FUnknownPtr runLoop; }; //------------------------------------------------------------------------------ struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject { X11::IEventHandler* handler { nullptr }; bool alive { false }; void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override; DELEGATE_REFCOUNT(Steinberg::FObject) DEFINE_INTERFACES DEF_INTERFACE(Steinberg::Linux::IEventHandler) END_DEFINE_INTERFACES(Steinberg::FObject) }; struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject { X11::ITimerHandler* handler { nullptr }; bool alive { false }; void PLUGIN_API onTimer() override; DELEGATE_REFCOUNT(Steinberg::FObject) DEFINE_INTERFACES DEF_INTERFACE(Steinberg::Linux::ITimerHandler) END_DEFINE_INTERFACES(Steinberg::FObject) }; //------------------------------------------------------------------------------ void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor) { SharedPointer runLoop = RunLoop::get(); if (!runLoop) { fprintf(stderr, "[x11] event has fired without active runloop\n"); return; } if (alive && handler) handler->onEvent(); } void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer() { SharedPointer runLoop = RunLoop::get(); if (!runLoop) { fprintf(stderr, "[x11] timer has fired without active runloop\n"); return; } if (alive && handler) handler->onTimer(); } //------------------------------------------------------------------------------ RunLoop::RunLoop(Steinberg::FUnknown* runLoop) : impl(new Impl) { impl->runLoop = runLoop; } RunLoop::~RunLoop() { //dumpCurrentState(); if (0) { // remove any leftover handlers for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { const auto& eh = impl->eventHandlers[i]; if (eh->alive && eh->handler) { impl->runLoop->unregisterEventHandler(eh.get()); } } for (size_t i = 0; i < impl->timerHandlers.size(); ++i) { const auto& th = impl->timerHandlers[i]; if (th->alive && th->handler) { impl->runLoop->unregisterTimer(th.get()); } } } } SharedPointer RunLoop::get() { return X11::RunLoop::get().cast(); } void RunLoop::processSomeEvents() { for (size_t i = 0; i < impl->eventHandlers.size(); ++i) { const auto& eh = impl->eventHandlers[i]; if (eh->alive && eh->handler) { eh->handler->onEvent(); } } } void RunLoop::dumpCurrentState() { fprintf(stderr, "=== X11 runloop ===\n"); fprintf(stderr, "\t" "Event slots:\n"); for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) { Impl::EventHandler *eh = impl->eventHandlers[i].get(); fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : ""); } fprintf(stderr, "\t" "Timer slots:\n"); for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) { Impl::TimerHandler *th = impl->timerHandlers[i].get(); fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : ""); } fprintf(stderr, "===/X11 runloop ===\n"); } template static void insertHandler(std::vector>& list, Steinberg::IPtr handler) { size_t i = 0; size_t n = list.size(); while (i < n && list[i]->alive) ++i; if (i < n) list[i] = handler; else list.emplace_back(handler); } template static size_t findHandler(const std::vector>& list, U* handler) { for (size_t i = 0, n = list.size(); i < n; ++i) { if (list[i]->alive && list[i]->handler == handler) return i; } return ~size_t(0); } bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler) { if (!impl->runLoop) return false; auto smtgHandler = Steinberg::owned(new Impl::EventHandler); smtgHandler->handler = handler; smtgHandler->alive = true; if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) { insertHandler(impl->eventHandlers, smtgHandler); return true; } return false; } bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler) { if (!impl->runLoop) return false; size_t index = findHandler(impl->eventHandlers, handler); if (index == ~size_t(0)) return false; Impl::EventHandler *eh = impl->eventHandlers[index].get(); if (impl->runLoop->unregisterEventHandler(eh) != Steinberg::kResultTrue) return false; eh->alive = false; return true; } bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler) { if (!impl->runLoop) return false; auto smtgHandler = Steinberg::owned(new Impl::TimerHandler); smtgHandler->handler = handler; smtgHandler->alive = true; if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) { insertHandler(impl->timerHandlers, smtgHandler); return true; } return false; } bool RunLoop::unregisterTimer(X11::ITimerHandler* handler) { if (!impl->runLoop) return false; size_t index = findHandler(impl->timerHandlers, handler); if (index == ~size_t(0)) return false; Impl::TimerHandler *th = impl->timerHandlers[index].get(); if (impl->runLoop->unregisterTimer(th) != Steinberg::kResultTrue) return false; th->alive = false; return true; } } // namespace VSTGUI #endif