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