1 /*
2 ** The various things we need to help with VST3 Linux
3 */
4
5 #if TARGET_VST3
6 #include <iostream>
7 #include <pthread.h>
8 #include <unistd.h>
9 #include "public.sdk/source/vst/vstguieditor.h"
10 #include "vstgui/lib/platform/platform_x11.h"
11 #include "vstgui/lib/platform/linux/x11platform.h"
12 #include "base/source/updatehandler.h"
13
14 using namespace VSTGUI;
15
16 static int ct = 0;
17 // Map Steinberg Vst Interface (Steinberg::Linux::IRunLoop) to VSTGUI Interface
18 // (VSTGUI::X11::RunLoop)
19 class RunLoop : public X11::IRunLoop, public AtomicReferenceCounted
20 {
21 public:
22 struct EventHandler : Steinberg::Linux::IEventHandler, public Steinberg::FObject
23 {
24 X11::IEventHandler *handler{nullptr};
25
onFDIsSetRunLoop::EventHandler26 void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override
27 {
28 // std::cout << __func__ << " " << handler << " " << ct++ << std::endl;
29 if (handler)
30 handler->onEvent();
31 // std::cout << __func__ << " END " << handler << std::endl;
32 }
33 DELEGATE_REFCOUNT(Steinberg::FObject)
34 DEFINE_INTERFACES
35 DEF_INTERFACE(Steinberg::Linux::IEventHandler)
36 END_DEFINE_INTERFACES(Steinberg::FObject)
37 };
38
39 struct TimerHandler : Steinberg::Linux::ITimerHandler, public Steinberg::FObject
40 {
41 X11::ITimerHandler *handler{nullptr};
42
onTimerRunLoop::TimerHandler43 void PLUGIN_API onTimer() final
44 {
45 // std::cout << __func__ << " " << handler << std::endl;
46 if (handler)
47 handler->onTimer();
48 // std::cout << __func__ << " END " << handler << std::endl;
49 }
50 DELEGATE_REFCOUNT(Steinberg::FObject)
51 DEFINE_INTERFACES
52 DEF_INTERFACE(Steinberg::Linux::ITimerHandler)
53 END_DEFINE_INTERFACES(Steinberg::FObject)
54 };
55
registerEventHandler(int fd,X11::IEventHandler * handler)56 bool registerEventHandler(int fd, X11::IEventHandler *handler) final
57 {
58 if (!runLoop)
59 return false;
60
61 auto smtgHandler = Steinberg::owned(new EventHandler());
62 smtgHandler->handler = handler;
63 if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue)
64 {
65 eventHandlers.push_back(smtgHandler);
66 return true;
67 }
68 return false;
69 }
70
unregisterEventHandler(X11::IEventHandler * handler)71 bool unregisterEventHandler(X11::IEventHandler *handler) final
72 {
73 if (!runLoop)
74 return false;
75
76 for (auto it = eventHandlers.begin(), end = eventHandlers.end(); it != end; ++it)
77 {
78 if ((*it)->handler == handler)
79 {
80 runLoop->unregisterEventHandler((*it));
81 eventHandlers.erase(it);
82 return true;
83 }
84 }
85 return false;
86 }
registerTimer(uint64_t interval,X11::ITimerHandler * handler)87 bool registerTimer(uint64_t interval, X11::ITimerHandler *handler) final
88 {
89 if (!runLoop)
90 return false;
91 // std::cout << "Have a runloop" << std::endl;
92
93 auto smtgHandler = Steinberg::owned(new TimerHandler());
94 smtgHandler->handler = handler;
95 if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue)
96 {
97 timerHandlers.push_back(smtgHandler);
98 return true;
99 }
100 return false;
101 }
unregisterTimer(X11::ITimerHandler * handler)102 bool unregisterTimer(X11::ITimerHandler *handler) final
103 {
104 if (!runLoop)
105 return false;
106
107 for (auto it = timerHandlers.begin(), end = timerHandlers.end(); it != end; ++it)
108 {
109 if ((*it)->handler == handler)
110 {
111 runLoop->unregisterTimer((*it));
112 timerHandlers.erase(it);
113 return true;
114 }
115 }
116 return false;
117 }
118
RunLoop(Steinberg::Linux::IRunLoop * _runLoop)119 RunLoop(Steinberg::Linux::IRunLoop *_runLoop) : runLoop(_runLoop)
120 {
121 // std::cout << "RunLoop is " << runLoop << " " << _runLoop << std::endl;
122 }
123
idle()124 void idle()
125 {
126 // See comment in LInuxVST3Idle below
127 for (auto h : eventHandlers)
128 h->handler->onEvent();
129 }
130
131 private:
132 using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
133 using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;
134 EventHandlers eventHandlers;
135 TimerHandlers timerHandlers;
136 // Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
137 Steinberg::Linux::IRunLoop *runLoop;
138 };
139
140 //-----------------------------------------------------------------------------
141 class UpdateHandlerInit
142 {
143 public:
UpdateHandlerInit()144 UpdateHandlerInit() { get(); }
get()145 Steinberg::UpdateHandler *get() { return Steinberg::UpdateHandler::instance(); }
146 };
147
148 static UpdateHandlerInit gUpdateHandlerInit;
149
150 //-----------------------------------------------------------------------------
151 class IdleUpdateHandler
152 {
153 public:
154 std::atomic<bool> running;
155 pthread_t t;
start()156 static void start()
157 {
158 auto &instance = get();
159 if (++instance.users == 1)
160 {
161 instance.running = true;
162 pthread_create(&instance.t, NULL, IdleUpdateHandler::doDefUp, NULL);
163 }
164 }
165
doDefUp(void * x)166 static void *doDefUp(void *x)
167 {
168 while (get().running)
169 {
170 // std::cout << "GUpdate" << std::endl;
171 gUpdateHandlerInit.get()->triggerDeferedUpdates();
172 usleep(1000 / 30.0);
173 }
174 return nullptr;
175 }
176
stop()177 static void stop()
178 {
179 auto &instance = get();
180 if (--instance.users == 0)
181 {
182 instance.running = false;
183 pthread_join(instance.t, NULL);
184 }
185 }
186
187 protected:
get()188 static IdleUpdateHandler &get()
189 {
190 static IdleUpdateHandler gInstance;
191 return gInstance;
192 }
193
194 VSTGUI::SharedPointer<VSTGUI::CVSTGUITimer> timer;
195 std::atomic<uint32_t> users{0};
196 };
197
LinuxVST3Init(Steinberg::Linux::IRunLoop * rl)198 void LinuxVST3Init(Steinberg::Linux::IRunLoop *rl)
199 {
200 // std::cout << "irl is " << rl << std::endl;
201 VSTGUI::X11::RunLoop::init(owned(new RunLoop(rl)));
202 }
203
LinuxVST3FrameOpen(VSTGUI::CFrame * that,void * parent,const VSTGUI::PlatformType & pt)204 void LinuxVST3FrameOpen(VSTGUI::CFrame *that, void *parent, const VSTGUI::PlatformType &pt)
205 {
206 IPlatformFrameConfig *config = nullptr;
207 X11::FrameConfig x11config;
208 x11config.runLoop = VSTGUI::X11::RunLoop::get();
209 config = &x11config;
210
211 // std::cout << "Special Magical VST3 Open " <<
212 // VSTGUI::X11::RunLoop::instance().getXcbConnection() << std::endl;
213 that->open(parent, pt, config);
214 // std::cout << "Are we done?" << std::endl;
215 IdleUpdateHandler::start();
216 }
217
LinuxVST3Detatch()218 void LinuxVST3Detatch()
219 {
220 IdleUpdateHandler::stop();
221
222 // We need to downcount the usage on the RunLoop to allow xcb to restart
223 VSTGUI::X11::RunLoop::exit();
224 }
225
LinuxVST3Idle()226 void LinuxVST3Idle()
227 {
228 /*
229 ** Why is this here? With the VST3 runloop we should be
230 ** parsimoniously notified by file descriptor polls and stuff right?
231 ** Well, of course, that doesn't work. If you listen to every
232 ** hover event somewhere in the bowls of vst3sdk, DAW runloops,
233 ** and so forth, you eventually hang up and lose events.
234 ** So what this does is, every idle, just give a kick to every
235 ** FDI poller to go and check. Basically just like VST2.
236 **
237 ** If you decide one day to eliminate this to optimize something
238 ** or another, make sure that in bitwig and the juce host that
239 ** as you mouse over hover assets they keep working forever and
240 ** don't hang up after 10-50 seconds. See #1817
241 */
242 auto xrlp = VSTGUI::X11::RunLoop::get().get();
243 auto rlp = dynamic_cast<RunLoop *>(xrlp);
244 if (rlp)
245 rlp->idle();
246 }
247 #endif
248