4 #if (!(defined(__APPLE__) || defined(_WIN32))) && __has_include(<xcb/xcb.h>)
5 #include <Vst3/UI/WindowContainer.hpp>
7 #include <ossia/detail/algorithms.hpp>
8 #include <ossia/detail/hash_map.hpp>
11 #include <QSocketNotifier>
15 #include <pluginterfaces/gui/iplugview.h>
26 namespace Linux = Steinberg::Linux;
31 : read{fd, QSocketNotifier::Read}
32 , write{fd, QSocketNotifier::Write}
33 , error{fd, QSocketNotifier::Exception}
37 void connect(QObject* sink, F f)
39 QObject::connect(&read, &QSocketNotifier::activated, sink, f);
40 QObject::connect(&write, &QSocketNotifier::activated, sink, f);
41 QObject::connect(&error, &QSocketNotifier::activated, sink, f);
43 read.setEnabled(
true);
44 write.setEnabled(
true);
45 error.setEnabled(
true);
48 QSocketNotifier write;
49 QSocketNotifier error;
55 xcb_connection_t* connection{};
58 std::unique_ptr<SocketPair> notifiers;
60 : xcb{dlopen(
"libxcb.so.1", RTLD_LOCAL)}
66 =
reinterpret_cast<decltype(&::xcb_connect)
>(dlsym(xcb,
"xcb_connect"));
69 auto xcb_get_file_descriptor
70 =
reinterpret_cast<decltype(&::xcb_get_file_descriptor)
>(
71 dlsym(xcb,
"xcb_get_file_descriptor"));
72 if(!xcb_get_file_descriptor)
75 connection = xcb_connect(
nullptr,
nullptr);
79 fd = xcb_get_file_descriptor(connection);
83 notifiers = std::make_unique<SocketPair>(fd);
94 =
reinterpret_cast<decltype(&::xcb_disconnect)
>(dlsym(xcb,
"xcb_disconnect"));
97 xcb_disconnect(connection);
103 Linux::IEventHandler* handler{};
104 QSocketNotifier* r_notifier{};
105 QSocketNotifier* w_notifier{};
106 QSocketNotifier* e_notifier{};
110 class GlobalSocketHandlers :
public QObject
115 ossia::hash_map<int, std::vector<Linux::IEventHandler*>> handlers;
116 GlobalSocketHandlers()
119 xcb.notifiers->connect(
this, [
this] { on_xcb(); });
124 if(
auto it = handlers.find(xcb.fd); it != handlers.end())
126 for(
auto& hdl : it->second)
127 hdl->onFDIsSet(xcb.fd);
131 void registerHandler(Linux::IEventHandler* handler,
int fd)
133 handlers[fd].push_back(handler);
135 void unregisterHandler(Linux::IEventHandler* handler,
int fd)
137 if(
auto it = handlers.find(fd); it != handlers.end())
139 ossia::remove_erase(it->second, handler);
140 if(it->second.empty())
144 static GlobalSocketHandlers& instance()
146 static GlobalSocketHandlers handlers;
150 class PlugFrame final
151 :
virtual public Steinberg::IPlugFrame
152 ,
virtual public Steinberg::Linux::IRunLoop
155 using TUID = Steinberg::TUID;
156 using FUID = Steinberg::FUID;
157 using uint32 = Steinberg::uint32;
158 using tresult = Steinberg::tresult;
160 QObject internalContextObject;
162 std::vector<std::pair<Linux::ITimerHandler*, QTimer*>> timers;
164 std::vector<SocketHandler> handlers;
166 tresult queryInterface(
const TUID _iid,
void** obj)
override
168 using namespace Steinberg;
169 if(FUnknownPrivate::iidEqual(_iid, FUnknown::iid))
174 if(FUnknownPrivate::iidEqual(_iid, IPlugFrame::iid))
179 if(FUnknownPrivate::iidEqual(_iid, Linux::IRunLoop::iid))
181 *obj =
static_cast<Linux::IRunLoop*
>(
this);
188 uint32 addRef()
override {
return 1; }
189 uint32 release()
override {
return 1; }
191 tresult registerEventHandler(Linux::IEventHandler* handler, Linux::FileDescriptor fd)
194 using namespace Steinberg;
196 return kInvalidArgument;
197 auto existing = ossia::find_if(handlers, [=](
auto p) {
return p.fd == fd; });
199 if(existing != handlers.end())
200 return kInvalidArgument;
202 auto readnotifier =
new QSocketNotifier{fd, QSocketNotifier::Read};
203 readnotifier->setEnabled(
true);
204 auto on_fd = [=] { handler->onFDIsSet(fd); };
206 readnotifier, &QSocketNotifier::activated, &internalContextObject, on_fd);
208 auto writenotifier =
new QSocketNotifier{fd, QSocketNotifier::Write};
209 writenotifier->setEnabled(
true);
211 writenotifier, &QSocketNotifier::activated, &internalContextObject, on_fd);
213 auto errnotifier =
new QSocketNotifier{fd, QSocketNotifier::Exception};
214 errnotifier->setEnabled(
true);
216 errnotifier, &QSocketNotifier::activated, &internalContextObject, on_fd);
219 SocketHandler{handler, readnotifier, writenotifier, errnotifier, fd});
220 GlobalSocketHandlers::instance().registerHandler(handler, fd);
225 tresult unregisterEventHandler(Linux::IEventHandler* handler) SMTG_OVERRIDE
227 using namespace Steinberg;
230 return kInvalidArgument;
233 tresult res{kResultFalse};
234 for(
auto it = handlers.begin(); it != handlers.end();)
236 if(it->handler == handler)
238 GlobalSocketHandlers::instance().unregisterHandler(handler, it->fd);
239 delete it->r_notifier;
240 delete it->w_notifier;
241 delete it->e_notifier;
242 it = handlers.erase(it);
254 tresult registerTimer(
255 Linux::ITimerHandler* handler, Linux::TimerInterval milliseconds)
override
258 QObject::connect(t, &QTimer::timeout, [=] { handler->onTimer(); });
259 t->start(milliseconds);
260 timers.push_back({handler, t});
261 return Steinberg::kResultOk;
264 tresult unregisterTimer(Linux::ITimerHandler* handler)
override
266 auto t = ossia::find_if(timers, [=](
auto& p1) {
return p1.first == handler; });
267 if(t != timers.end())
272 return Steinberg::kResultOk;
277 explicit PlugFrame(QDialog& w, WindowContainer wc)
285 for(
auto timer : timers)
287 for(
auto handler : handlers)
289 GlobalSocketHandlers::instance().unregisterHandler(handler.handler, handler.fd);
290 delete handler.r_notifier;
291 delete handler.w_notifier;
292 delete handler.e_notifier;
296 tresult resizeView(Steinberg::IPlugView* view, Steinberg::ViewRect* newSize)
override
298 wc.setSizeFromVst(*view, *newSize, w);
299 return Steinberg::kResultOk;