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