sfizz-ui/plugins/vst/X11RunLoop.cpp

232 lines
6.6 KiB
C++
Raw Normal View History

// 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 <vector>
#include <typeinfo>
#include <cstdio>
#include <cassert>
namespace VSTGUI {
struct RunLoop::Impl {
struct EventHandler;
struct TimerHandler;
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
EventHandlers eventHandlers;
TimerHandlers timerHandlers;
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> 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 = 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 = 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> RunLoop::get()
{
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
}
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 <class T>
static void insertHandler(std::vector<Steinberg::IPtr<T>>& list, Steinberg::IPtr<T> 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 <class T, class U>
static size_t findHandler(const std::vector<Steinberg::IPtr<T>>& 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