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 #include "DistrhoUIInternal.hpp"
18 
19 #include "../extra/String.hpp"
20 
21 #include "lv2/atom.h"
22 #include "lv2/atom-util.h"
23 #include "lv2/data-access.h"
24 #include "lv2/instance-access.h"
25 #include "lv2/midi.h"
26 #include "lv2/options.h"
27 #include "lv2/parameters.h"
28 #include "lv2/patch.h"
29 #include "lv2/ui.h"
30 #include "lv2/urid.h"
31 #include "lv2/lv2_kxstudio_properties.h"
32 #include "lv2/lv2_programs.h"
33 
34 #ifndef DISTRHO_PLUGIN_LV2_STATE_PREFIX
35 # define DISTRHO_PLUGIN_LV2_STATE_PREFIX "urn:distrho:"
36 #endif
37 
38 START_NAMESPACE_DISTRHO
39 
40 typedef struct _LV2_Atom_MidiEvent {
41     LV2_Atom atom;    /**< Atom header. */
42     uint8_t  data[3]; /**< MIDI data (body). */
43 } LV2_Atom_MidiEvent;
44 
45 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
46 static const sendNoteFunc sendNoteCallback = nullptr;
47 #endif
48 
49 // -----------------------------------------------------------------------
50 
51 template <class LV2F>
getLv2Feature(const LV2_Feature * const * features,const char * const uri)52 static const LV2F* getLv2Feature(const LV2_Feature* const* features, const char* const uri)
53 {
54     for (int i=0; features[i] != nullptr; ++i)
55     {
56         if (std::strcmp(features[i]->URI, uri) == 0)
57             return (const LV2F*)features[i]->data;
58     }
59 
60     return nullptr;
61 }
62 
63 class UiLv2
64 {
65 public:
UiLv2(const char * const bundlePath,const intptr_t winId,const LV2_Options_Option * options,const LV2_URID_Map * const uridMap,const LV2_Feature * const * const features,const LV2UI_Controller controller,const LV2UI_Write_Function writeFunc,LV2UI_Widget * const widget,void * const dspPtr,const float scaleFactor,const uint32_t bgColor,const uint32_t fgColor)66     UiLv2(const char* const bundlePath,
67           const intptr_t winId,
68           const LV2_Options_Option* options,
69           const LV2_URID_Map* const uridMap,
70           const LV2_Feature* const* const features,
71           const LV2UI_Controller controller,
72           const LV2UI_Write_Function writeFunc,
73           LV2UI_Widget* const widget,
74           void* const dspPtr,
75           const float scaleFactor,
76           const uint32_t bgColor,
77           const uint32_t fgColor)
78         : fUI(this, winId,
79               editParameterCallback,
80               setParameterCallback,
81               setStateCallback,
82               sendNoteCallback,
83               setSizeCallback,
84               fileRequestCallback,
85               bundlePath,
86               dspPtr,
87               scaleFactor,
88               bgColor,
89               fgColor),
90           fUridMap(uridMap),
91           fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
92           fUiResize(getLv2Feature<LV2UI_Resize>(features, LV2_UI__resize)),
93           fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
94           fController(controller),
95           fWriteFunction(writeFunc),
96           fURIDs(uridMap),
97           fWinIdWasNull(winId == 0)
98     {
99         if (fUiResize != nullptr && winId != 0)
100             fUiResize->ui_resize(fUiResize->handle, fUI.getWidth(), fUI.getHeight());
101 
102         if (widget != nullptr)
103             *widget = (LV2UI_Widget)fUI.getWindowId();
104 
105 #if DISTRHO_PLUGIN_WANT_STATE
106         // tell the DSP we're ready to receive msgs
107         setState("__dpf_ui_data__", "");
108 #endif
109 
110         if (winId != 0)
111             return;
112 
113         // if winId == 0 then options must not be null
114         DISTRHO_SAFE_ASSERT_RETURN(options != nullptr,);
115 
116         const LV2_URID uridWindowTitle    = uridMap->map(uridMap->handle, LV2_UI__windowTitle);
117         const LV2_URID uridTransientWinId = uridMap->map(uridMap->handle, LV2_KXSTUDIO_PROPERTIES__TransientWindowId);
118 
119         bool hasTitle = false;
120 
121         for (int i=0; options[i].key != 0; ++i)
122         {
123             if (options[i].key == uridTransientWinId)
124             {
125                 if (options[i].type == fURIDs.atomLong)
126                 {
127                     if (const int64_t transientWinId = *(const int64_t*)options[i].value)
128                         fUI.setWindowTransientWinId(static_cast<intptr_t>(transientWinId));
129                 }
130                 else
131                     d_stderr("Host provides transientWinId but has wrong value type");
132             }
133             else if (options[i].key == uridWindowTitle)
134             {
135                 if (options[i].type == fURIDs.atomString)
136                 {
137                     if (const char* const windowTitle = (const char*)options[i].value)
138                     {
139                         hasTitle = true;
140                         fUI.setWindowTitle(windowTitle);
141                     }
142                 }
143                 else
144                     d_stderr("Host provides windowTitle but has wrong value type");
145             }
146         }
147 
148         if (! hasTitle)
149             fUI.setWindowTitle(DISTRHO_PLUGIN_NAME);
150     }
151 
152     // -------------------------------------------------------------------
153 
lv2ui_port_event(const uint32_t rindex,const uint32_t bufferSize,const uint32_t format,const void * const buffer)154     void lv2ui_port_event(const uint32_t rindex, const uint32_t bufferSize, const uint32_t format, const void* const buffer)
155     {
156         if (format == 0)
157         {
158             const uint32_t parameterOffset = fUI.getParameterOffset();
159 
160             if (rindex < parameterOffset)
161                 return;
162 
163             DISTRHO_SAFE_ASSERT_RETURN(bufferSize == sizeof(float),)
164 
165             const float value = *(const float*)buffer;
166             fUI.parameterChanged(rindex-parameterOffset, value);
167         }
168 #if DISTRHO_PLUGIN_WANT_STATE
169         else if (format == fURIDs.atomEventTransfer)
170         {
171             const LV2_Atom* const atom = (const LV2_Atom*)buffer;
172 
173             if (atom->type == fURIDs.dpfKeyValue)
174             {
175                 const char* const key   = (const char*)LV2_ATOM_BODY_CONST(atom);
176                 const char* const value = key+(std::strlen(key)+1);
177 
178                 fUI.stateChanged(key, value);
179             }
180             else
181             {
182                 d_stdout("received atom not dpfKeyValue");
183             }
184         }
185 #endif
186     }
187 
188     // -------------------------------------------------------------------
189 
lv2ui_idle()190     int lv2ui_idle()
191     {
192         if (fWinIdWasNull)
193             return (fUI.idle() && fUI.isVisible()) ? 0 : 1;
194 
195         return fUI.idle() ? 0 : 1;
196     }
197 
lv2ui_show()198     int lv2ui_show()
199     {
200         return fUI.setWindowVisible(true) ? 0 : 1;
201     }
202 
lv2ui_hide()203     int lv2ui_hide()
204     {
205         return fUI.setWindowVisible(false) ? 0 : 1;
206     }
207 
lv2ui_resize(uint width,uint height)208     int lv2ui_resize(uint width, uint height)
209     {
210         fUI.setWindowSize(width, height, true);
211         return 0;
212     }
213 
214     // -------------------------------------------------------------------
215 
lv2_get_options(LV2_Options_Option * const)216     uint32_t lv2_get_options(LV2_Options_Option* const /*options*/)
217     {
218         // currently unused
219         return LV2_OPTIONS_ERR_UNKNOWN;
220     }
221 
lv2_set_options(const LV2_Options_Option * const options)222     uint32_t lv2_set_options(const LV2_Options_Option* const options)
223     {
224         for (int i=0; options[i].key != 0; ++i)
225         {
226             if (options[i].key == fURIDs.paramSampleRate)
227             {
228                 if (options[i].type == fURIDs.atomFloat)
229                 {
230                     const float sampleRate = *(const float*)options[i].value;
231                     fUI.setSampleRate(sampleRate);
232                     continue;
233                 }
234                 else
235                 {
236                     d_stderr("Host changed UI sample-rate but with wrong value type");
237                     continue;
238                 }
239             }
240         }
241 
242         return LV2_OPTIONS_SUCCESS;
243     }
244 
245     // -------------------------------------------------------------------
246 
247 #if DISTRHO_PLUGIN_WANT_PROGRAMS
lv2ui_select_program(const uint32_t bank,const uint32_t program)248     void lv2ui_select_program(const uint32_t bank, const uint32_t program)
249     {
250         const uint32_t realProgram = bank * 128 + program;
251 
252         fUI.programLoaded(realProgram);
253     }
254 #endif
255 
256     // -------------------------------------------------------------------
257 
258 protected:
editParameterValue(const uint32_t rindex,const bool started)259     void editParameterValue(const uint32_t rindex, const bool started)
260     {
261         if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
262             fUiTouch->touch(fUiTouch->handle, rindex, started);
263     }
264 
setParameterValue(const uint32_t rindex,const float value)265     void setParameterValue(const uint32_t rindex, const float value)
266     {
267         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
268 
269         fWriteFunction(fController, rindex, sizeof(float), 0, &value);
270     }
271 
setState(const char * const key,const char * const value)272     void setState(const char* const key, const char* const value)
273     {
274         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
275 
276         const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
277 
278         // join key and value
279         String tmpStr;
280         tmpStr += key;
281         tmpStr += "\xff";
282         tmpStr += value;
283 
284         tmpStr[std::strlen(key)] = '\0';
285 
286         // set msg size (key + separator + value + null terminator)
287         const size_t msgSize = tmpStr.length() + 1U;
288 
289         // reserve atom space
290         const size_t atomSize = sizeof(LV2_Atom) + msgSize;
291         char         atomBuf[atomSize];
292         std::memset(atomBuf, 0, atomSize);
293 
294         // set atom info
295         LV2_Atom* const atom = (LV2_Atom*)atomBuf;
296         atom->size = msgSize;
297         atom->type = fURIDs.dpfKeyValue;
298 
299         // set atom data
300         std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);
301 
302         // send to DSP side
303         fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
304     }
305 
306 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
sendNote(const uint8_t channel,const uint8_t note,const uint8_t velocity)307     void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
308     {
309         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
310 
311         if (channel > 0xF)
312             return;
313 
314         const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
315 
316         LV2_Atom_MidiEvent atomMidiEvent;
317         atomMidiEvent.atom.size = 3;
318         atomMidiEvent.atom.type = fMidiEventURID;
319 
320         atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80);
321         atomMidiEvent.data[1] = note;
322         atomMidiEvent.data[2] = velocity;
323 
324         // send to DSP side
325         fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
326                        fURIDs.atomEventTransfer, &atomMidiEvent);
327     }
328 #endif
329 
setSize(const uint width,const uint height)330     void setSize(const uint width, const uint height)
331     {
332         fUI.setWindowSize(width, height);
333 
334         if (fUiResize != nullptr && ! fWinIdWasNull)
335             fUiResize->ui_resize(fUiResize->handle, width, height);
336     }
337 
fileRequest(const char * const key)338     bool fileRequest(const char* const key)
339     {
340         d_stdout("UI file request %s %p", key, fUiRequestValue);
341 
342         if (fUiRequestValue == nullptr)
343             return false;
344 
345         String dpf_lv2_key(DISTRHO_PLUGIN_URI "#");
346         dpf_lv2_key += key;
347 
348         const int r = fUiRequestValue->request(fUiRequestValue->handle,
349                                         fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
350                                         fURIDs.atomPath,
351                                         nullptr);
352 
353         d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r);
354         return r == LV2UI_REQUEST_VALUE_SUCCESS;
355     }
356 
357 private:
358     UIExporter fUI;
359 
360     // LV2 features
361     const LV2_URID_Map*        const fUridMap;
362     const LV2UI_Request_Value* const fUiRequestValue;
363     const LV2UI_Resize*        const fUiResize;
364     const LV2UI_Touch*         const fUiTouch;
365 
366     // LV2 UI stuff
367     const LV2UI_Controller     fController;
368     const LV2UI_Write_Function fWriteFunction;
369 
370     // LV2 URIDs
371     const struct URIDs {
372         const LV2_URID_Map* _uridMap;
373         LV2_URID dpfKeyValue;
374         LV2_URID atomEventTransfer;
375         LV2_URID atomFloat;
376         LV2_URID atomLong;
377         LV2_URID atomPath;
378         LV2_URID atomString;
379         LV2_URID midiEvent;
380         LV2_URID paramSampleRate;
381         LV2_URID patchSet;
382 
URIDsUiLv2::URIDs383         URIDs(const LV2_URID_Map* const uridMap)
384             : _uridMap(uridMap),
385               dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
386               atomEventTransfer(map(LV2_ATOM__eventTransfer)),
387               atomFloat(map(LV2_ATOM__Float)),
388               atomLong(map(LV2_ATOM__Long)),
389               atomPath(map(LV2_ATOM__Path)),
390               atomString(map(LV2_ATOM__String)),
391               midiEvent(map(LV2_MIDI__MidiEvent)),
392               paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
393               patchSet(map(LV2_PATCH__Set)) {}
394 
mapUiLv2::URIDs395         inline LV2_URID map(const char* const uri) const
396         {
397             return _uridMap->map(_uridMap->handle, uri);
398         }
399     } fURIDs;
400 
401     // using ui:showInterface if true
402     bool fWinIdWasNull;
403 
404     // -------------------------------------------------------------------
405     // Callbacks
406 
407     #define uiPtr ((UiLv2*)ptr)
408 
editParameterCallback(void * ptr,uint32_t rindex,bool started)409     static void editParameterCallback(void* ptr, uint32_t rindex, bool started)
410     {
411         uiPtr->editParameterValue(rindex, started);
412     }
413 
setParameterCallback(void * ptr,uint32_t rindex,float value)414     static void setParameterCallback(void* ptr, uint32_t rindex, float value)
415     {
416         uiPtr->setParameterValue(rindex, value);
417     }
418 
setStateCallback(void * ptr,const char * key,const char * value)419     static void setStateCallback(void* ptr, const char* key, const char* value)
420     {
421         uiPtr->setState(key, value);
422     }
423 
424 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
sendNoteCallback(void * ptr,uint8_t channel,uint8_t note,uint8_t velocity)425     static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
426     {
427         uiPtr->sendNote(channel, note, velocity);
428     }
429 #endif
430 
setSizeCallback(void * ptr,uint width,uint height)431     static void setSizeCallback(void* ptr, uint width, uint height)
432     {
433         uiPtr->setSize(width, height);
434     }
435 
fileRequestCallback(void * ptr,const char * key)436     static bool fileRequestCallback(void* ptr, const char* key)
437     {
438         return uiPtr->fileRequest(key);
439     }
440 
441     #undef uiPtr
442 };
443 
444 // -----------------------------------------------------------------------
445 
lv2ui_instantiate(const LV2UI_Descriptor *,const char * const uri,const char * const bundlePath,const LV2UI_Write_Function writeFunction,const LV2UI_Controller controller,LV2UI_Widget * const widget,const LV2_Feature * const * const features)446 static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
447                                       const char* const uri,
448                                       const char* const bundlePath,
449                                       const LV2UI_Write_Function writeFunction,
450                                       const LV2UI_Controller controller,
451                                       LV2UI_Widget* const widget,
452                                       const LV2_Feature* const* const features)
453 {
454     if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
455     {
456         d_stderr("Invalid plugin URI");
457         return nullptr;
458     }
459 
460     const LV2_Options_Option* options   = nullptr;
461     const LV2_URID_Map*       uridMap   = nullptr;
462     void*                     parentId  = nullptr;
463     void*                     instance  = nullptr;
464 
465 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
466     struct LV2_DirectAccess_Interface {
467         void* (*get_instance_pointer)(LV2_Handle handle);
468     };
469     const LV2_Extension_Data_Feature* extData = nullptr;
470 #endif
471 
472     for (int i=0; features[i] != nullptr; ++i)
473     {
474         /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
475             options = (const LV2_Options_Option*)features[i]->data;
476         else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
477             uridMap = (const LV2_URID_Map*)features[i]->data;
478         else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
479             parentId = features[i]->data;
480 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
481         else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
482             extData = (const LV2_Extension_Data_Feature*)features[i]->data;
483         else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
484             instance = features[i]->data;
485 #endif
486     }
487 
488     if (options == nullptr && parentId == nullptr)
489     {
490         d_stderr("Options feature missing (needed for show-interface), cannot continue!");
491         return nullptr;
492     }
493 
494     if (uridMap == nullptr)
495     {
496         d_stderr("URID Map feature missing, cannot continue!");
497         return nullptr;
498     }
499 
500     if (parentId == nullptr)
501     {
502         d_stdout("Parent Window Id missing, host should be using ui:showInterface...");
503     }
504 
505 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
506     if (extData == nullptr || instance == nullptr)
507     {
508         d_stderr("Data or instance access missing, cannot continue!");
509         return nullptr;
510     }
511 
512     if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access"))
513         instance = directAccess->get_instance_pointer(instance);
514     else
515         instance = nullptr;
516 
517     if (instance == nullptr)
518     {
519         d_stderr("Failed to get direct access, cannot continue!");
520         return nullptr;
521     }
522 #endif
523 
524     const intptr_t winId = (intptr_t)parentId;
525     float scaleFactor = 1.0f;
526     uint32_t bgColor = 0;
527     uint32_t fgColor = 0xffffffff;
528 
529     if (options != nullptr)
530     {
531         const LV2_URID uridAtomInt     = uridMap->map(uridMap->handle, LV2_ATOM__Int);
532         const LV2_URID uridAtomFloat   = uridMap->map(uridMap->handle, LV2_ATOM__Float);
533         const LV2_URID uridSampleRate  = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
534         const LV2_URID uridBgColor     = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
535         const LV2_URID uridFgColor     = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
536         const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
537 
538         for (int i=0; options[i].key != 0; ++i)
539         {
540             /**/ if (options[i].key == uridSampleRate)
541             {
542                 if (options[i].type == uridAtomFloat)
543                     d_lastUiSampleRate = *(const float*)options[i].value;
544                 else
545                     d_stderr("Host provides UI sample-rate but has wrong value type");
546             }
547             else if (options[i].key == uridScaleFactor)
548             {
549                 if (options[i].type == uridAtomFloat)
550                     scaleFactor = *(const float*)options[i].value;
551                 else
552                     d_stderr("Host provides UI scale factor but has wrong value type");
553             }
554             else if (options[i].key == uridBgColor)
555             {
556                 if (options[i].type == uridAtomInt)
557                     bgColor = (uint32_t)*(const int32_t*)options[i].value;
558                 else
559                     d_stderr("Host provides UI background color but has wrong value type");
560             }
561             else if (options[i].key == uridFgColor)
562             {
563                 if (options[i].type == uridAtomInt)
564                     fgColor = (uint32_t)*(const int32_t*)options[i].value;
565                 else
566                     d_stderr("Host provides UI foreground color but has wrong value type");
567             }
568         }
569     }
570 
571     if (d_lastUiSampleRate < 1.0)
572     {
573         d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)");
574         d_lastUiSampleRate = 44100.0;
575     }
576 
577     return new UiLv2(bundlePath, winId, options, uridMap, features,
578                      controller, writeFunction, widget, instance,
579                      scaleFactor, bgColor, fgColor);
580 }
581 
582 #define uiPtr ((UiLv2*)ui)
583 
lv2ui_cleanup(LV2UI_Handle ui)584 static void lv2ui_cleanup(LV2UI_Handle ui)
585 {
586     delete uiPtr;
587 }
588 
lv2ui_port_event(LV2UI_Handle ui,uint32_t portIndex,uint32_t bufferSize,uint32_t format,const void * buffer)589 static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
590 {
591     uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
592 }
593 
594 // -----------------------------------------------------------------------
595 
lv2ui_idle(LV2UI_Handle ui)596 static int lv2ui_idle(LV2UI_Handle ui)
597 {
598     return uiPtr->lv2ui_idle();
599 }
600 
lv2ui_show(LV2UI_Handle ui)601 static int lv2ui_show(LV2UI_Handle ui)
602 {
603     return uiPtr->lv2ui_show();
604 }
605 
lv2ui_hide(LV2UI_Handle ui)606 static int lv2ui_hide(LV2UI_Handle ui)
607 {
608     return uiPtr->lv2ui_hide();
609 }
610 
lv2ui_resize(LV2UI_Handle ui,int width,int height)611 static int lv2ui_resize(LV2UI_Handle ui, int width, int height)
612 {
613     DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 1);
614     DISTRHO_SAFE_ASSERT_RETURN(width > 0, 1);
615     DISTRHO_SAFE_ASSERT_RETURN(height > 0, 1);
616 
617     return 1; // This needs more testing
618     //return uiPtr->lv2ui_resize(width, height);
619 }
620 
621 // -----------------------------------------------------------------------
622 
lv2_get_options(LV2UI_Handle ui,LV2_Options_Option * options)623 static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
624 {
625     return uiPtr->lv2_get_options(options);
626 }
627 
lv2_set_options(LV2UI_Handle ui,const LV2_Options_Option * options)628 static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options)
629 {
630     return uiPtr->lv2_set_options(options);
631 }
632 
633 // -----------------------------------------------------------------------
634 
635 #if DISTRHO_PLUGIN_WANT_PROGRAMS
lv2ui_select_program(LV2UI_Handle ui,uint32_t bank,uint32_t program)636 static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
637 {
638     uiPtr->lv2ui_select_program(bank, program);
639 }
640 #endif
641 
642 // -----------------------------------------------------------------------
643 
lv2ui_extension_data(const char * uri)644 static const void* lv2ui_extension_data(const char* uri)
645 {
646     static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
647     static const LV2UI_Idle_Interface  uiIdle  = { lv2ui_idle };
648     static const LV2UI_Show_Interface  uiShow  = { lv2ui_show, lv2ui_hide };
649     static const LV2UI_Resize          uiResz  = { nullptr, lv2ui_resize };
650 
651     if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
652         return &options;
653     if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
654         return &uiIdle;
655     if (std::strcmp(uri, LV2_UI__showInterface) == 0)
656         return &uiShow;
657     if (std::strcmp(uri, LV2_UI__resize) == 0)
658         return &uiResz;
659 
660 #if DISTRHO_PLUGIN_WANT_PROGRAMS
661     static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program };
662 
663     if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
664         return &uiPrograms;
665 #endif
666 
667     return nullptr;
668 }
669 
670 #undef instancePtr
671 
672 // -----------------------------------------------------------------------
673 
674 static const LV2UI_Descriptor sLv2UiDescriptor = {
675     DISTRHO_UI_URI,
676     lv2ui_instantiate,
677     lv2ui_cleanup,
678     lv2ui_port_event,
679     lv2ui_extension_data
680 };
681 
682 // -----------------------------------------------------------------------
683 
684 END_NAMESPACE_DISTRHO
685 
686 DISTRHO_PLUGIN_EXPORT
lv2ui_descriptor(uint32_t index)687 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
688 {
689     USE_NAMESPACE_DISTRHO
690     return (index == 0) ? &sLv2UiDescriptor : nullptr;
691 }
692 
693 // -----------------------------------------------------------------------
694