1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DISTRHO_UI_INTERNAL_HPP_INCLUDED
18 #define DISTRHO_UI_INTERNAL_HPP_INCLUDED
19 
20 #include "../DistrhoUI.hpp"
21 
22 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
23 # include "../extra/Sleep.hpp"
24 using DGL_NAMESPACE::IdleCallback;
25 #else
26 # include "../../dgl/Application.hpp"
27 # include "../../dgl/Window.hpp"
28 using DGL_NAMESPACE::Application;
29 using DGL_NAMESPACE::IdleCallback;
30 using DGL_NAMESPACE::Window;
31 #endif
32 
33 START_NAMESPACE_DISTRHO
34 
35 // -----------------------------------------------------------------------
36 // Static data, see DistrhoUI.cpp
37 
38 extern double      d_lastUiSampleRate;
39 extern void*       d_lastUiDspPtr;
40 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
41 extern const char* g_nextBundlePath;
42 extern double      g_nextScaleFactor;
43 extern uintptr_t   g_nextWindowId;
44 #else
45 extern Window*     d_lastUiWindow;
46 #endif
47 
48 // -----------------------------------------------------------------------
49 // UI callbacks
50 
51 typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
52 typedef void (*setParamFunc)  (void* ptr, uint32_t rindex, float value);
53 typedef void (*setStateFunc)  (void* ptr, const char* key, const char* value);
54 typedef void (*sendNoteFunc)  (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
55 typedef void (*setSizeFunc)   (void* ptr, uint width, uint height);
56 
57 // -----------------------------------------------------------------------
58 // UI private data
59 
60 struct UI::PrivateData {
61     // DSP
62     double   sampleRate;
63     uint32_t parameterOffset;
64 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
65     void*    dspPtr;
66 #endif
67 
68     // UI
69     bool automaticallyScale;
70     bool resizeInProgress;
71     uint minWidth;
72     uint minHeight;
73 
74     // Callbacks
75     void*         callbacksPtr;
76     editParamFunc editParamCallbackFunc;
77     setParamFunc  setParamCallbackFunc;
78     setStateFunc  setStateCallbackFunc;
79     sendNoteFunc  sendNoteCallbackFunc;
80     setSizeFunc   setSizeCallbackFunc;
81 
PrivateDataUI::PrivateData82     PrivateData() noexcept
83         : sampleRate(d_lastUiSampleRate),
84           parameterOffset(0),
85 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
86           dspPtr(d_lastUiDspPtr),
87 #endif
88           automaticallyScale(false),
89           resizeInProgress(false),
90           minWidth(0),
91           minHeight(0),
92           callbacksPtr(nullptr),
93           editParamCallbackFunc(nullptr),
94           setParamCallbackFunc(nullptr),
95           setStateCallbackFunc(nullptr),
96           sendNoteCallbackFunc(nullptr),
97           setSizeCallbackFunc(nullptr)
98     {
99         DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
100 
101 #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
102         parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
103 # if DISTRHO_PLUGIN_WANT_LATENCY
104         parameterOffset += 1;
105 # endif
106 #endif
107 
108 #ifdef DISTRHO_PLUGIN_TARGET_LV2
109 # if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
110         parameterOffset += 1;
111 #  if DISTRHO_PLUGIN_WANT_STATE
112         parameterOffset += 1;
113 #  endif
114 # endif
115 #endif
116     }
117 
editParamCallbackUI::PrivateData118     void editParamCallback(const uint32_t rindex, const bool started)
119     {
120         if (editParamCallbackFunc != nullptr)
121             editParamCallbackFunc(callbacksPtr, rindex, started);
122     }
123 
setParamCallbackUI::PrivateData124     void setParamCallback(const uint32_t rindex, const float value)
125     {
126         if (setParamCallbackFunc != nullptr)
127             setParamCallbackFunc(callbacksPtr, rindex, value);
128     }
129 
setStateCallbackUI::PrivateData130     void setStateCallback(const char* const key, const char* const value)
131     {
132         if (setStateCallbackFunc != nullptr)
133             setStateCallbackFunc(callbacksPtr, key, value);
134     }
135 
sendNoteCallbackUI::PrivateData136     void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
137     {
138         if (sendNoteCallbackFunc != nullptr)
139             sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
140     }
141 
setSizeCallbackUI::PrivateData142     void setSizeCallback(const uint width, const uint height)
143     {
144         if (setSizeCallbackFunc != nullptr)
145             setSizeCallbackFunc(callbacksPtr, width, height);
146     }
147 };
148 
149 // -----------------------------------------------------------------------
150 // Plugin Window, needed to take care of resize properly
151 
152 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
153 static inline
createUiWrapper(void * const dspPtr,const uintptr_t winId,const double scaleFactor,const char * const bundlePath)154 UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath)
155 {
156     d_lastUiDspPtr    = dspPtr;
157     g_nextWindowId    = winId;
158     g_nextScaleFactor = scaleFactor;
159     g_nextBundlePath  = bundlePath;
160     UI* const ret     = createUI();
161     d_lastUiDspPtr    = nullptr;
162     g_nextWindowId    = 0;
163     g_nextScaleFactor = 1.0;
164     g_nextBundlePath  = nullptr;
165     return ret;
166 }
167 #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
168 static inline
createUiWrapper(void * const dspPtr,Window * const window)169 UI* createUiWrapper(void* const dspPtr, Window* const window)
170 {
171     d_lastUiDspPtr = dspPtr;
172     d_lastUiWindow = window;
173     UI* const ret  = createUI();
174     d_lastUiDspPtr = nullptr;
175     d_lastUiWindow = nullptr;
176     return ret;
177 }
178 
179 class UIExporterWindow : public Window
180 {
181 public:
UIExporterWindow(Application & app,const intptr_t winId,const double scaleFactor,void * const dspPtr)182     UIExporterWindow(Application& app, const intptr_t winId, const double scaleFactor, void* const dspPtr)
183         : Window(app, winId, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
184           fUI(createUiWrapper(dspPtr, this)),
185           fIsReady(false)
186     {
187         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
188         DISTRHO_SAFE_ASSERT_RETURN(fUI->pData != nullptr,);
189 
190         setSize(fUI->getWidth(), fUI->getHeight());
191     }
192 
~UIExporterWindow()193     ~UIExporterWindow()
194     {
195         delete fUI;
196     }
197 
getUI() const198     UI* getUI() const noexcept
199     {
200         return fUI;
201     }
202 
isReady() const203     bool isReady() const noexcept
204     {
205         return fIsReady;
206     }
207 
208 protected:
209     // custom window reshape
onReshape(uint width,uint height)210     void onReshape(uint width, uint height) override
211     {
212         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
213 
214         UI::PrivateData* const pData = fUI->pData;
215         DISTRHO_SAFE_ASSERT_RETURN(pData != nullptr,);
216 
217         if (pData->automaticallyScale)
218         {
219             const double scaleHorizontal = static_cast<double>(width) / static_cast<double>(pData->minWidth);
220             const double scaleVertical   = static_cast<double>(height) / static_cast<double>(pData->minHeight);
221             _setAutoScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical);
222         }
223 
224         pData->resizeInProgress = true;
225         fUI->setSize(width, height);
226         pData->resizeInProgress = false;
227 
228         fUI->uiReshape(width, height);
229         fIsReady = true;
230     }
231 
232 # ifndef DGL_FILE_BROWSER_DISABLED
233     // custom file-browser selected
fileBrowserSelected(const char * filename)234     void fileBrowserSelected(const char* filename) override
235     {
236         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
237 
238         fUI->uiFileBrowserSelected(filename);
239     }
240 # endif
241 
242 private:
243     UI* const fUI;
244     bool fIsReady;
245 };
246 #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
247 
248 // -----------------------------------------------------------------------
249 // UI exporter class
250 
251 class UIExporter
252 {
253 public:
UIExporter(void * const callbacksPtr,const intptr_t winId,const editParamFunc editParamCall,const setParamFunc setParamCall,const setStateFunc setStateCall,const sendNoteFunc sendNoteCall,const setSizeFunc setSizeCall,const float scaleFactor=1.0f,void * const dspPtr=nullptr,const char * const bundlePath=nullptr)254     UIExporter(void* const callbacksPtr,
255                const intptr_t winId,
256                const editParamFunc editParamCall,
257                const setParamFunc setParamCall,
258                const setStateFunc setStateCall,
259                const sendNoteFunc sendNoteCall,
260                const setSizeFunc setSizeCall,
261                const float scaleFactor = 1.0f,
262                void* const dspPtr = nullptr,
263                const char* const bundlePath = nullptr)
264 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
265         : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)),
266 #else
267         : glApp(),
268           glWindow(glApp, winId, scaleFactor, dspPtr),
269           fChangingSize(false),
270           fUI(glWindow.getUI()),
271 #endif
272           fData((fUI != nullptr) ? fUI->pData : nullptr)
273     {
274         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
275         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
276 
277         fData->callbacksPtr          = callbacksPtr;
278         fData->editParamCallbackFunc = editParamCall;
279         fData->setParamCallbackFunc  = setParamCall;
280         fData->setStateCallbackFunc  = setStateCall;
281         fData->sendNoteCallbackFunc  = sendNoteCall;
282         fData->setSizeCallbackFunc   = setSizeCall;
283 
284 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
285         // unused
286         return; (void)bundlePath;
287 #endif
288     }
289 
290 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
~UIExporter()291     ~UIExporter()
292     {
293         delete fUI;
294     }
295 #endif
296 
297     // -------------------------------------------------------------------
298 
299 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
getWidth() const300     uint getWidth() const noexcept
301     {
302         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
303         return fUI->getWidth();
304     }
305 
getHeight() const306     uint getHeight() const noexcept
307     {
308         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
309         return fUI->getHeight();
310     }
311 
isVisible() const312     bool isVisible() const noexcept
313     {
314         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
315         return fUI->isRunning();
316     }
317 
getWindowId() const318     intptr_t getWindowId() const noexcept
319     {
320         return 0;
321     }
322 #else
getWidth() const323     uint getWidth() const noexcept
324     {
325         return glWindow.getWidth();
326     }
327 
getHeight() const328     uint getHeight() const noexcept
329     {
330         return glWindow.getHeight();
331     }
332 
isVisible() const333     bool isVisible() const noexcept
334     {
335         return glWindow.isVisible();
336     }
337 
getWindowId() const338     intptr_t getWindowId() const noexcept
339     {
340         return glWindow.getWindowId();
341     }
342 #endif
343 
344     // -------------------------------------------------------------------
345 
getParameterOffset() const346     uint32_t getParameterOffset() const noexcept
347     {
348         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
349 
350         return fData->parameterOffset;
351     }
352 
353     // -------------------------------------------------------------------
354 
parameterChanged(const uint32_t index,const float value)355     void parameterChanged(const uint32_t index, const float value)
356     {
357         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
358 
359         fUI->parameterChanged(index, value);
360     }
361 
362 #if DISTRHO_PLUGIN_WANT_PROGRAMS
programLoaded(const uint32_t index)363     void programLoaded(const uint32_t index)
364     {
365         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
366 
367         fUI->programLoaded(index);
368     }
369 #endif
370 
371 #if DISTRHO_PLUGIN_WANT_STATE
stateChanged(const char * const key,const char * const value)372     void stateChanged(const char* const key, const char* const value)
373     {
374         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
375         DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
376         DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,);
377 
378         fUI->stateChanged(key, value);
379     }
380 #endif
381 
382     // -------------------------------------------------------------------
383 
384 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
exec(IdleCallback * const cb)385     void exec(IdleCallback* const cb)
386     {
387         DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
388         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
389 
390         fUI->setVisible(true);
391         cb->idleCallback();
392 
393         while (fUI->isRunning())
394         {
395             d_msleep(10);
396             cb->idleCallback();
397         }
398     }
399 
exec_idle()400     void exec_idle()
401     {
402     }
403 
idle()404     bool idle()
405     {
406         return true;
407     }
408 
focus()409     void focus()
410     {
411     }
412 
quit()413     void quit()
414     {
415         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
416 
417         fUI->setVisible(false);
418         fUI->terminateAndWaitForProcess();
419     }
420 #else
exec(IdleCallback * const cb)421     void exec(IdleCallback* const cb)
422     {
423         DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
424         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
425 
426         glWindow.addIdleCallback(cb);
427         glWindow.setVisible(true);
428         glApp.exec();
429     }
430 
exec_idle()431     void exec_idle()
432     {
433         if (glWindow.isReady())
434             fUI->uiIdle();
435     }
436 
focus()437     void focus()
438     {
439         glWindow.focus();
440     }
441 
idle()442     bool idle()
443     {
444         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
445 
446         glApp.idle();
447 
448         if (glWindow.isReady())
449             fUI->uiIdle();
450 
451         return ! glApp.isQuiting();
452     }
453 
quit()454     void quit()
455     {
456         glWindow.close();
457         glApp.quit();
458     }
459 
460 #endif
461     // -------------------------------------------------------------------
462 
463 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
setWindowTitle(const char * const uiTitle)464     void setWindowTitle(const char* const uiTitle)
465     {
466         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
467 
468         fUI->setTitle(uiTitle);
469     }
470 
setWindowSize(const uint width,const uint height,const bool=false)471     void setWindowSize(const uint width, const uint height, const bool = false)
472     {
473         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
474 
475         fUI->setSize(width, height);
476     }
477 
setWindowTransientWinId(const uintptr_t winId)478     void setWindowTransientWinId(const uintptr_t winId)
479     {
480         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
481 
482         fUI->setTransientWinId(winId);
483     }
484 
setWindowVisible(const bool yesNo)485     bool setWindowVisible(const bool yesNo)
486     {
487         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
488 
489         fUI->setVisible(yesNo);
490 
491         return fUI->isRunning();
492     }
493 #else
setWindowTitle(const char * const uiTitle)494     void setWindowTitle(const char* const uiTitle)
495     {
496         glWindow.setTitle(uiTitle);
497     }
498 
setWindowSize(const uint width,const uint height,const bool updateUI=false)499     void setWindowSize(const uint width, const uint height, const bool updateUI = false)
500     {
501         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
502         DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,);
503 
504         fChangingSize = true;
505 
506         if (updateUI)
507             fUI->setSize(width, height);
508 
509         glWindow.setSize(width, height);
510 
511         fChangingSize = false;
512     }
513 
setWindowTransientWinId(const uintptr_t winId)514     void setWindowTransientWinId(const uintptr_t winId)
515     {
516         glWindow.setTransientWinId(winId);
517     }
518 
setWindowVisible(const bool yesNo)519     bool setWindowVisible(const bool yesNo)
520     {
521         glWindow.setVisible(yesNo);
522 
523         return ! glApp.isQuiting();
524     }
525 
handlePluginKeyboard(const bool press,const uint key)526     bool handlePluginKeyboard(const bool press, const uint key)
527     {
528         return glWindow.handlePluginKeyboard(press, key);
529     }
530 
handlePluginSpecial(const bool press,const DGL_NAMESPACE::Key key)531     bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key)
532     {
533         return glWindow.handlePluginSpecial(press, key);
534     }
535 #endif
536 
537     // -------------------------------------------------------------------
538 
setSampleRate(const double sampleRate,const bool doCallback=false)539     void setSampleRate(const double sampleRate, const bool doCallback = false)
540     {
541         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
542         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
543         DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
544 
545         if (d_isEqual(fData->sampleRate, sampleRate))
546             return;
547 
548         fData->sampleRate = sampleRate;
549 
550         if (doCallback)
551             fUI->sampleRateChanged(sampleRate);
552     }
553 
554 private:
555 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
556     // -------------------------------------------------------------------
557     // DGL Application and Window for this widget
558 
559     Application      glApp;
560     UIExporterWindow glWindow;
561 
562     // prevent recursion
563     bool fChangingSize;
564 #endif
565 
566     // -------------------------------------------------------------------
567     // Widget and DistrhoUI data
568 
569     UI* const fUI;
570     UI::PrivateData* const fData;
571 
572     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter)
573 };
574 
575 // -----------------------------------------------------------------------
576 
577 END_NAMESPACE_DISTRHO
578 
579 #endif // DISTRHO_UI_INTERNAL_HPP_INCLUDED
580