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