1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2020 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 typedef bool (*fileRequestFunc) (void* ptr, const char* key);
57 
58 // -----------------------------------------------------------------------
59 // UI private data
60 
61 struct UI::PrivateData {
62     // DSP
63     double   sampleRate;
64     uint32_t parameterOffset;
65 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
66     void*    dspPtr;
67 #endif
68 
69     // UI
70     bool automaticallyScale;
71     bool resizeInProgress;
72     uint minWidth;
73     uint minHeight;
74     uint bgColor;
75     uint fgColor;
76 
77     // Callbacks
78     void*           callbacksPtr;
79     editParamFunc   editParamCallbackFunc;
80     setParamFunc    setParamCallbackFunc;
81     setStateFunc    setStateCallbackFunc;
82     sendNoteFunc    sendNoteCallbackFunc;
83     setSizeFunc     setSizeCallbackFunc;
84     fileRequestFunc fileRequestCallbackFunc;
85 
PrivateDataUI::PrivateData86     PrivateData() noexcept
87         : sampleRate(d_lastUiSampleRate),
88           parameterOffset(0),
89 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
90           dspPtr(d_lastUiDspPtr),
91 #endif
92           automaticallyScale(false),
93           resizeInProgress(false),
94           minWidth(0),
95           minHeight(0),
96           bgColor(0),
97           fgColor(0),
98           callbacksPtr(nullptr),
99           editParamCallbackFunc(nullptr),
100           setParamCallbackFunc(nullptr),
101           setStateCallbackFunc(nullptr),
102           sendNoteCallbackFunc(nullptr),
103           setSizeCallbackFunc(nullptr),
104           fileRequestCallbackFunc(nullptr)
105     {
106         DISTRHO_SAFE_ASSERT(d_isNotZero(sampleRate));
107 
108 #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
109         parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
110 # if DISTRHO_PLUGIN_WANT_LATENCY
111         parameterOffset += 1;
112 # endif
113 #endif
114 
115 #ifdef DISTRHO_PLUGIN_TARGET_LV2
116 # if (DISTRHO_PLUGIN_IS_SYNTH || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
117         parameterOffset += 1;
118 #  if DISTRHO_PLUGIN_WANT_STATE
119         parameterOffset += 1;
120 #  endif
121 # endif
122 #endif
123     }
124 
editParamCallbackUI::PrivateData125     void editParamCallback(const uint32_t rindex, const bool started)
126     {
127         if (editParamCallbackFunc != nullptr)
128             editParamCallbackFunc(callbacksPtr, rindex, started);
129     }
130 
setParamCallbackUI::PrivateData131     void setParamCallback(const uint32_t rindex, const float value)
132     {
133         if (setParamCallbackFunc != nullptr)
134             setParamCallbackFunc(callbacksPtr, rindex, value);
135     }
136 
setStateCallbackUI::PrivateData137     void setStateCallback(const char* const key, const char* const value)
138     {
139         if (setStateCallbackFunc != nullptr)
140             setStateCallbackFunc(callbacksPtr, key, value);
141     }
142 
sendNoteCallbackUI::PrivateData143     void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
144     {
145         if (sendNoteCallbackFunc != nullptr)
146             sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
147     }
148 
setSizeCallbackUI::PrivateData149     void setSizeCallback(const uint width, const uint height)
150     {
151         if (setSizeCallbackFunc != nullptr)
152             setSizeCallbackFunc(callbacksPtr, width, height);
153     }
154 
fileRequestCallbackUI::PrivateData155     bool fileRequestCallback(const char* key)
156     {
157         if (fileRequestCallbackFunc != nullptr)
158             return fileRequestCallbackFunc(callbacksPtr, key);
159 
160         // TODO use old style DPF dialog here
161 
162         return false;
163     }
164 };
165 
166 // -----------------------------------------------------------------------
167 // Plugin Window, needed to take care of resize properly
168 
169 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
170 static inline
createUiWrapper(void * const dspPtr,const uintptr_t winId,const double scaleFactor,const char * const bundlePath)171 UI* createUiWrapper(void* const dspPtr, const uintptr_t winId, const double scaleFactor, const char* const bundlePath)
172 {
173     d_lastUiDspPtr    = dspPtr;
174     g_nextWindowId    = winId;
175     g_nextScaleFactor = scaleFactor;
176     g_nextBundlePath  = bundlePath;
177     UI* const ret     = createUI();
178     d_lastUiDspPtr    = nullptr;
179     g_nextWindowId    = 0;
180     g_nextScaleFactor = 1.0;
181     g_nextBundlePath  = nullptr;
182     return ret;
183 }
184 #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
185 static inline
createUiWrapper(void * const dspPtr,Window * const window)186 UI* createUiWrapper(void* const dspPtr, Window* const window)
187 {
188     d_lastUiDspPtr = dspPtr;
189     d_lastUiWindow = window;
190     UI* const ret  = createUI();
191     d_lastUiDspPtr = nullptr;
192     d_lastUiWindow = nullptr;
193     return ret;
194 }
195 
196 class UIExporterWindow : public Window
197 {
198 public:
UIExporterWindow(Application & app,const intptr_t winId,const double scaleFactor,void * const dspPtr)199     UIExporterWindow(Application& app, const intptr_t winId, const double scaleFactor, void* const dspPtr)
200         : Window(app, winId, scaleFactor, DISTRHO_UI_USER_RESIZABLE),
201           fUI(createUiWrapper(dspPtr, this)),
202           fIsReady(false)
203     {
204         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
205         DISTRHO_SAFE_ASSERT_RETURN(fUI->pData != nullptr,);
206 
207         setSize(fUI->getWidth(), fUI->getHeight());
208     }
209 
~UIExporterWindow()210     ~UIExporterWindow()
211     {
212         delete fUI;
213     }
214 
getUI() const215     UI* getUI() const noexcept
216     {
217         return fUI;
218     }
219 
isReady() const220     bool isReady() const noexcept
221     {
222         return fIsReady;
223     }
224 
225 protected:
226     // custom window reshape
onReshape(uint width,uint height)227     void onReshape(uint width, uint height) override
228     {
229         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
230 
231         UI::PrivateData* const pData = fUI->pData;
232         DISTRHO_SAFE_ASSERT_RETURN(pData != nullptr,);
233 
234         if (pData->automaticallyScale)
235         {
236             const double scaleHorizontal = static_cast<double>(width) / static_cast<double>(pData->minWidth);
237             const double scaleVertical   = static_cast<double>(height) / static_cast<double>(pData->minHeight);
238             _setAutoScaling(scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical);
239         }
240 
241         pData->resizeInProgress = true;
242         fUI->setSize(width, height);
243         pData->resizeInProgress = false;
244 
245         fUI->uiReshape(width, height);
246         fIsReady = true;
247     }
248 
249 # ifndef DGL_FILE_BROWSER_DISABLED
250     // custom file-browser selected
fileBrowserSelected(const char * filename)251     void fileBrowserSelected(const char* filename) override
252     {
253         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
254 
255         fUI->uiFileBrowserSelected(filename);
256     }
257 # endif
258 
259 private:
260     UI* const fUI;
261     bool fIsReady;
262 };
263 #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
264 
265 // -----------------------------------------------------------------------
266 // UI exporter class
267 
268 class UIExporter
269 {
270 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 fileRequestFunc fileRequestCall,const char * const bundlePath=nullptr,void * const dspPtr=nullptr,const float scaleFactor=1.0f,const uint32_t bgColor=0,const uint32_t fgColor=0xffffffff)271     UIExporter(void* const callbacksPtr,
272                const intptr_t winId,
273                const editParamFunc editParamCall,
274                const setParamFunc setParamCall,
275                const setStateFunc setStateCall,
276                const sendNoteFunc sendNoteCall,
277                const setSizeFunc setSizeCall,
278                const fileRequestFunc fileRequestCall,
279                const char* const bundlePath = nullptr,
280                void* const dspPtr = nullptr,
281                const float scaleFactor = 1.0f,
282                const uint32_t bgColor = 0,
283                const uint32_t fgColor = 0xffffffff)
284 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
285         : fUI(createUiWrapper(dspPtr, winId, scaleFactor, bundlePath)),
286 #else
287         : glApp(),
288           glWindow(glApp, winId, scaleFactor, dspPtr),
289           fChangingSize(false),
290           fUI(glWindow.getUI()),
291 #endif
292           fData((fUI != nullptr) ? fUI->pData : nullptr)
293     {
294         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
295         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
296 
297         fData->bgColor = bgColor;
298         fData->fgColor = fgColor;
299 
300         fData->callbacksPtr            = callbacksPtr;
301         fData->editParamCallbackFunc   = editParamCall;
302         fData->setParamCallbackFunc    = setParamCall;
303         fData->setStateCallbackFunc    = setStateCall;
304         fData->sendNoteCallbackFunc    = sendNoteCall;
305         fData->setSizeCallbackFunc     = setSizeCall;
306         fData->fileRequestCallbackFunc = fileRequestCall;
307 
308 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
309         // unused
310         return; (void)bundlePath;
311 #endif
312     }
313 
314 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
~UIExporter()315     ~UIExporter()
316     {
317         delete fUI;
318     }
319 #endif
320 
321     // -------------------------------------------------------------------
322 
323 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
getWidth() const324     uint getWidth() const noexcept
325     {
326         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
327         return fUI->getWidth();
328     }
329 
getHeight() const330     uint getHeight() const noexcept
331     {
332         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, 1);
333         return fUI->getHeight();
334     }
335 
isVisible() const336     bool isVisible() const noexcept
337     {
338         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
339         return fUI->isRunning();
340     }
341 
getWindowId() const342     intptr_t getWindowId() const noexcept
343     {
344         return 0;
345     }
346 #else
getWidth() const347     uint getWidth() const noexcept
348     {
349         return glWindow.getWidth();
350     }
351 
getHeight() const352     uint getHeight() const noexcept
353     {
354         return glWindow.getHeight();
355     }
356 
isVisible() const357     bool isVisible() const noexcept
358     {
359         return glWindow.isVisible();
360     }
361 
getWindowId() const362     intptr_t getWindowId() const noexcept
363     {
364         return glWindow.getWindowId();
365     }
366 #endif
367 
getBackgroundColor() const368     uint getBackgroundColor() const noexcept
369     {
370         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
371 
372         return fData->bgColor;
373     }
374 
getForegroundColor() const375     uint getForegroundColor() const noexcept
376     {
377         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0xffffffff);
378 
379         return fData->fgColor;
380     }
381 
382     // -------------------------------------------------------------------
383 
getParameterOffset() const384     uint32_t getParameterOffset() const noexcept
385     {
386         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0);
387 
388         return fData->parameterOffset;
389     }
390 
391     // -------------------------------------------------------------------
392 
parameterChanged(const uint32_t index,const float value)393     void parameterChanged(const uint32_t index, const float value)
394     {
395         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
396 
397         fUI->parameterChanged(index, value);
398     }
399 
400 #if DISTRHO_PLUGIN_WANT_PROGRAMS
programLoaded(const uint32_t index)401     void programLoaded(const uint32_t index)
402     {
403         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
404 
405         fUI->programLoaded(index);
406     }
407 #endif
408 
409 #if DISTRHO_PLUGIN_WANT_STATE
stateChanged(const char * const key,const char * const value)410     void stateChanged(const char* const key, const char* const value)
411     {
412         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
413         DISTRHO_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
414         DISTRHO_SAFE_ASSERT_RETURN(value != nullptr,);
415 
416         fUI->stateChanged(key, value);
417     }
418 #endif
419 
420     // -------------------------------------------------------------------
421 
422 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
exec(IdleCallback * const cb)423     void exec(IdleCallback* const cb)
424     {
425         DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
426         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
427 
428         fUI->setVisible(true);
429         cb->idleCallback();
430 
431         while (fUI->isRunning())
432         {
433             d_msleep(10);
434             cb->idleCallback();
435         }
436     }
437 
exec_idle()438     void exec_idle()
439     {
440     }
441 
idle()442     bool idle()
443     {
444         return true;
445     }
446 
focus()447     void focus()
448     {
449     }
450 
quit()451     void quit()
452     {
453         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
454 
455         fUI->setVisible(false);
456         fUI->terminateAndWaitForProcess();
457     }
458 #else
exec(IdleCallback * const cb)459     void exec(IdleCallback* const cb)
460     {
461         DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
462         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
463 
464         glWindow.addIdleCallback(cb);
465         glWindow.setVisible(true);
466         glApp.exec();
467     }
468 
exec_idle()469     void exec_idle()
470     {
471         if (glWindow.isReady())
472             fUI->uiIdle();
473     }
474 
focus()475     void focus()
476     {
477         glWindow.focus();
478     }
479 
idle()480     bool idle()
481     {
482         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
483 
484         glApp.idle();
485 
486         if (glWindow.isReady())
487             fUI->uiIdle();
488 
489         return ! glApp.isQuiting();
490     }
491 
quit()492     void quit()
493     {
494         glWindow.close();
495         glApp.quit();
496     }
497 
498 #endif
499     // -------------------------------------------------------------------
500 
501 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
setWindowTitle(const char * const uiTitle)502     void setWindowTitle(const char* const uiTitle)
503     {
504         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
505 
506         fUI->setTitle(uiTitle);
507     }
508 
setWindowSize(const uint width,const uint height,const bool=false)509     void setWindowSize(const uint width, const uint height, const bool = false)
510     {
511         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
512 
513         fUI->setSize(width, height);
514     }
515 
setWindowTransientWinId(const uintptr_t winId)516     void setWindowTransientWinId(const uintptr_t winId)
517     {
518         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
519 
520         fUI->setTransientWinId(winId);
521     }
522 
setWindowVisible(const bool yesNo)523     bool setWindowVisible(const bool yesNo)
524     {
525         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr, false);
526 
527         fUI->setVisible(yesNo);
528 
529         return fUI->isRunning();
530     }
531 #else
setWindowTitle(const char * const uiTitle)532     void setWindowTitle(const char* const uiTitle)
533     {
534         glWindow.setTitle(uiTitle);
535     }
536 
setWindowSize(const uint width,const uint height,const bool updateUI=false)537     void setWindowSize(const uint width, const uint height, const bool updateUI = false)
538     {
539         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
540         DISTRHO_SAFE_ASSERT_RETURN(! fChangingSize,);
541 
542         fChangingSize = true;
543 
544         if (updateUI)
545             fUI->setSize(width, height);
546 
547         glWindow.setSize(width, height);
548 
549         fChangingSize = false;
550     }
551 
setWindowTransientWinId(const uintptr_t winId)552     void setWindowTransientWinId(const uintptr_t winId)
553     {
554         glWindow.setTransientWinId(winId);
555     }
556 
setWindowVisible(const bool yesNo)557     bool setWindowVisible(const bool yesNo)
558     {
559         glWindow.setVisible(yesNo);
560 
561         return ! glApp.isQuiting();
562     }
563 
handlePluginKeyboard(const bool press,const uint key)564     bool handlePluginKeyboard(const bool press, const uint key)
565     {
566         return glWindow.handlePluginKeyboard(press, key);
567     }
568 
handlePluginSpecial(const bool press,const DGL_NAMESPACE::Key key)569     bool handlePluginSpecial(const bool press, const DGL_NAMESPACE::Key key)
570     {
571         return glWindow.handlePluginSpecial(press, key);
572     }
573 #endif
574 
575     // -------------------------------------------------------------------
576 
setSampleRate(const double sampleRate,const bool doCallback=false)577     void setSampleRate(const double sampleRate, const bool doCallback = false)
578     {
579         DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,);
580         DISTRHO_SAFE_ASSERT_RETURN(fUI != nullptr,);
581         DISTRHO_SAFE_ASSERT(sampleRate > 0.0);
582 
583         if (d_isEqual(fData->sampleRate, sampleRate))
584             return;
585 
586         fData->sampleRate = sampleRate;
587 
588         if (doCallback)
589             fUI->sampleRateChanged(sampleRate);
590     }
591 
592 private:
593 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
594     // -------------------------------------------------------------------
595     // DGL Application and Window for this widget
596 
597     Application      glApp;
598     UIExporterWindow glWindow;
599 
600     // prevent recursion
601     bool fChangingSize;
602 #endif
603 
604     // -------------------------------------------------------------------
605     // Widget and DistrhoUI data
606 
607     UI* const fUI;
608     UI::PrivateData* const fData;
609 
610     DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(UIExporter)
611 };
612 
613 // -----------------------------------------------------------------------
614 
615 END_NAMESPACE_DISTRHO
616 
617 #endif // DISTRHO_UI_INTERNAL_HPP_INCLUDED
618