1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2021 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 sampleRate,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 sampleRate,
76           const float scaleFactor,
77           const uint32_t bgColor,
78           const uint32_t fgColor)
79         : fUI(this, winId, sampleRate,
80               editParameterCallback,
81               setParameterCallback,
82               setStateCallback,
83               sendNoteCallback,
84               nullptr, // resize is very messy, hosts can do it without extensions
85               fileRequestCallback,
86               bundlePath,
87               dspPtr,
88               scaleFactor,
89               bgColor,
90               fgColor),
91           fUridMap(uridMap),
92           fUiPortMap(getLv2Feature<LV2UI_Port_Map>(features, LV2_UI__portMap)),
93           fUiRequestValue(getLv2Feature<LV2UI_Request_Value>(features, LV2_UI__requestValue)),
94           fUiTouch(getLv2Feature<LV2UI_Touch>(features, LV2_UI__touch)),
95           fController(controller),
96           fWriteFunction(writeFunc),
97           fURIDs(uridMap),
98           fBypassParameterIndex(fUiPortMap != nullptr ? fUiPortMap->port_index(fUiPortMap->handle, "lv2_enabled")
99                                                       : LV2UI_INVALID_PORT_INDEX),
100           fWinIdWasNull(winId == 0)
101     {
102         if (widget != nullptr)
103             *widget = (LV2UI_Widget)fUI.getNativeWindowHandle();
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             float value = *(const float*)buffer;
166 
167             if (rindex == fBypassParameterIndex)
168                 value = 1.0f - value;
169 
170             fUI.parameterChanged(rindex-parameterOffset, value);
171         }
172 #if DISTRHO_PLUGIN_WANT_STATE
173         else if (format == fURIDs.atomEventTransfer)
174         {
175             const LV2_Atom* const atom = (const LV2_Atom*)buffer;
176 
177             if (atom->type == fURIDs.dpfKeyValue)
178             {
179                 const char* const key   = (const char*)LV2_ATOM_BODY_CONST(atom);
180                 const char* const value = key+(std::strlen(key)+1);
181 
182                 fUI.stateChanged(key, value);
183             }
184             else
185             {
186                 d_stdout("received atom not dpfKeyValue");
187             }
188         }
189 #endif
190     }
191 
192     // -------------------------------------------------------------------
193 
lv2ui_idle()194     int lv2ui_idle()
195     {
196         if (fWinIdWasNull)
197             return (fUI.plugin_idle() && fUI.isVisible()) ? 0 : 1;
198 
199         return fUI.plugin_idle() ? 0 : 1;
200     }
201 
lv2ui_show()202     int lv2ui_show()
203     {
204         return fUI.setWindowVisible(true) ? 0 : 1;
205     }
206 
lv2ui_hide()207     int lv2ui_hide()
208     {
209         return fUI.setWindowVisible(false) ? 0 : 1;
210     }
211 
212     // -------------------------------------------------------------------
213 
lv2_get_options(LV2_Options_Option * const)214     uint32_t lv2_get_options(LV2_Options_Option* const /*options*/)
215     {
216         // currently unused
217         return LV2_OPTIONS_ERR_UNKNOWN;
218     }
219 
lv2_set_options(const LV2_Options_Option * const options)220     uint32_t lv2_set_options(const LV2_Options_Option* const options)
221     {
222         for (int i=0; options[i].key != 0; ++i)
223         {
224             if (options[i].key == fURIDs.paramSampleRate)
225             {
226                 if (options[i].type == fURIDs.atomFloat)
227                 {
228                     const float sampleRate = *(const float*)options[i].value;
229                     fUI.setSampleRate(sampleRate);
230                     continue;
231                 }
232                 else
233                 {
234                     d_stderr("Host changed UI sample-rate but with wrong value type");
235                     continue;
236                 }
237             }
238         }
239 
240         return LV2_OPTIONS_SUCCESS;
241     }
242 
243     // -------------------------------------------------------------------
244 
245 #if DISTRHO_PLUGIN_WANT_PROGRAMS
lv2ui_select_program(const uint32_t bank,const uint32_t program)246     void lv2ui_select_program(const uint32_t bank, const uint32_t program)
247     {
248         const uint32_t realProgram = bank * 128 + program;
249 
250         fUI.programLoaded(realProgram);
251     }
252 #endif
253 
254     // -------------------------------------------------------------------
255 
256 protected:
editParameterValue(const uint32_t rindex,const bool started)257     void editParameterValue(const uint32_t rindex, const bool started)
258     {
259         if (fUiTouch != nullptr && fUiTouch->touch != nullptr)
260             fUiTouch->touch(fUiTouch->handle, rindex, started);
261     }
262 
setParameterValue(const uint32_t rindex,float value)263     void setParameterValue(const uint32_t rindex, float value)
264     {
265         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
266 
267         if (rindex == fBypassParameterIndex)
268             value = 1.0f - value;
269 
270         fWriteFunction(fController, rindex, sizeof(float), 0, &value);
271     }
272 
setState(const char * const key,const char * const value)273     void setState(const char* const key, const char* const value)
274     {
275         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
276 
277         const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
278 
279         // join key and value
280         String tmpStr;
281         tmpStr += key;
282         tmpStr += "\xff";
283         tmpStr += value;
284 
285         tmpStr[std::strlen(key)] = '\0';
286 
287         // set msg size (key + separator + value + null terminator)
288         const size_t msgSize = tmpStr.length() + 1U;
289 
290         // reserve atom space
291         const size_t atomSize = sizeof(LV2_Atom) + msgSize;
292         char* const  atomBuf = (char*)malloc(atomSize);
293         std::memset(atomBuf, 0, atomSize);
294 
295         // set atom info
296         LV2_Atom* const atom = (LV2_Atom*)atomBuf;
297         atom->size = msgSize;
298         atom->type = fURIDs.dpfKeyValue;
299 
300         // set atom data
301         std::memcpy(atomBuf + sizeof(LV2_Atom), tmpStr.buffer(), msgSize);
302 
303         // send to DSP side
304         fWriteFunction(fController, eventInPortIndex, atomSize, fURIDs.atomEventTransfer, atom);
305 
306         // free atom space
307         free(atomBuf);
308     }
309 
310 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
sendNote(const uint8_t channel,const uint8_t note,const uint8_t velocity)311     void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
312     {
313         DISTRHO_SAFE_ASSERT_RETURN(fWriteFunction != nullptr,);
314 
315         if (channel > 0xF)
316             return;
317 
318         const uint32_t eventInPortIndex = DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
319 
320         LV2_Atom_MidiEvent atomMidiEvent;
321         atomMidiEvent.atom.size = 3;
322         atomMidiEvent.atom.type = fURIDs.midiEvent;
323 
324         atomMidiEvent.data[0] = channel + (velocity != 0 ? 0x90 : 0x80);
325         atomMidiEvent.data[1] = note;
326         atomMidiEvent.data[2] = velocity;
327 
328         // send to DSP side
329         fWriteFunction(fController, eventInPortIndex, lv2_atom_total_size(&atomMidiEvent.atom),
330                        fURIDs.atomEventTransfer, &atomMidiEvent);
331     }
332 #endif
333 
fileRequest(const char * const key)334     bool fileRequest(const char* const key)
335     {
336         d_stdout("UI file request %s %p", key, fUiRequestValue);
337 
338         if (fUiRequestValue == nullptr)
339             return false;
340 
341         String dpf_lv2_key(DISTRHO_PLUGIN_URI "#");
342         dpf_lv2_key += key;
343 
344         const int r = fUiRequestValue->request(fUiRequestValue->handle,
345                                         fUridMap->map(fUridMap->handle, dpf_lv2_key.buffer()),
346                                         fURIDs.atomPath,
347                                         nullptr);
348 
349         d_stdout("UI file request %s %p => %s %i", key, fUiRequestValue, dpf_lv2_key.buffer(), r);
350         return r == LV2UI_REQUEST_VALUE_SUCCESS;
351     }
352 
353 private:
354     UIExporter fUI;
355 
356     // LV2 features
357     const LV2_URID_Map*        const fUridMap;
358     const LV2UI_Port_Map*      const fUiPortMap;
359     const LV2UI_Request_Value* const fUiRequestValue;
360     const LV2UI_Touch*         const fUiTouch;
361 
362     // LV2 UI stuff
363     const LV2UI_Controller     fController;
364     const LV2UI_Write_Function fWriteFunction;
365 
366     // LV2 URIDs
367     const struct URIDs {
368         const LV2_URID_Map* _uridMap;
369         LV2_URID dpfKeyValue;
370         LV2_URID atomEventTransfer;
371         LV2_URID atomFloat;
372         LV2_URID atomLong;
373         LV2_URID atomPath;
374         LV2_URID atomString;
375         LV2_URID midiEvent;
376         LV2_URID paramSampleRate;
377         LV2_URID patchSet;
378 
URIDsUiLv2::URIDs379         URIDs(const LV2_URID_Map* const uridMap)
380             : _uridMap(uridMap),
381               dpfKeyValue(map(DISTRHO_PLUGIN_LV2_STATE_PREFIX "KeyValueState")),
382               atomEventTransfer(map(LV2_ATOM__eventTransfer)),
383               atomFloat(map(LV2_ATOM__Float)),
384               atomLong(map(LV2_ATOM__Long)),
385               atomPath(map(LV2_ATOM__Path)),
386               atomString(map(LV2_ATOM__String)),
387               midiEvent(map(LV2_MIDI__MidiEvent)),
388               paramSampleRate(map(LV2_PARAMETERS__sampleRate)),
389               patchSet(map(LV2_PATCH__Set)) {}
390 
mapUiLv2::URIDs391         inline LV2_URID map(const char* const uri) const
392         {
393             return _uridMap->map(_uridMap->handle, uri);
394         }
395     } fURIDs;
396 
397     // index of bypass parameter, if present
398     const uint32_t fBypassParameterIndex;
399 
400     // using ui:showInterface if true
401     const bool fWinIdWasNull;
402 
403     // -------------------------------------------------------------------
404     // Callbacks
405 
406     #define uiPtr ((UiLv2*)ptr)
407 
editParameterCallback(void * ptr,uint32_t rindex,bool started)408     static void editParameterCallback(void* ptr, uint32_t rindex, bool started)
409     {
410         uiPtr->editParameterValue(rindex, started);
411     }
412 
setParameterCallback(void * ptr,uint32_t rindex,float value)413     static void setParameterCallback(void* ptr, uint32_t rindex, float value)
414     {
415         uiPtr->setParameterValue(rindex, value);
416     }
417 
setStateCallback(void * ptr,const char * key,const char * value)418     static void setStateCallback(void* ptr, const char* key, const char* value)
419     {
420         uiPtr->setState(key, value);
421     }
422 
423 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
sendNoteCallback(void * ptr,uint8_t channel,uint8_t note,uint8_t velocity)424     static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
425     {
426         uiPtr->sendNote(channel, note, velocity);
427     }
428 #endif
429 
fileRequestCallback(void * ptr,const char * key)430     static bool fileRequestCallback(void* ptr, const char* key)
431     {
432         return uiPtr->fileRequest(key);
433     }
434 
435     #undef uiPtr
436 };
437 
438 // -----------------------------------------------------------------------
439 
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)440 static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*,
441                                       const char* const uri,
442                                       const char* const bundlePath,
443                                       const LV2UI_Write_Function writeFunction,
444                                       const LV2UI_Controller controller,
445                                       LV2UI_Widget* const widget,
446                                       const LV2_Feature* const* const features)
447 {
448     if (uri == nullptr || std::strcmp(uri, DISTRHO_PLUGIN_URI) != 0)
449     {
450         d_stderr("Invalid plugin URI");
451         return nullptr;
452     }
453 
454     const LV2_Options_Option* options   = nullptr;
455     const LV2_URID_Map*       uridMap   = nullptr;
456     void*                     parentId  = nullptr;
457     void*                     instance  = nullptr;
458 
459 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
460     struct LV2_DirectAccess_Interface {
461         void* (*get_instance_pointer)(LV2_Handle handle);
462     };
463     const LV2_Extension_Data_Feature* extData = nullptr;
464 #endif
465 
466     for (int i=0; features[i] != nullptr; ++i)
467     {
468         /**/ if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0)
469             options = (const LV2_Options_Option*)features[i]->data;
470         else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0)
471             uridMap = (const LV2_URID_Map*)features[i]->data;
472         else if (std::strcmp(features[i]->URI, LV2_UI__parent) == 0)
473             parentId = features[i]->data;
474 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
475         else if (std::strcmp(features[i]->URI, LV2_DATA_ACCESS_URI) == 0)
476             extData = (const LV2_Extension_Data_Feature*)features[i]->data;
477         else if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0)
478             instance = features[i]->data;
479 #endif
480     }
481 
482     if (options == nullptr && parentId == nullptr)
483     {
484         d_stderr("Options feature missing (needed for show-interface), cannot continue!");
485         return nullptr;
486     }
487 
488     if (uridMap == nullptr)
489     {
490         d_stderr("URID Map feature missing, cannot continue!");
491         return nullptr;
492     }
493 
494     if (parentId == nullptr)
495     {
496         d_stdout("Parent Window Id missing, host should be using ui:showInterface...");
497     }
498 
499 #if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS
500     if (extData == nullptr || instance == nullptr)
501     {
502         d_stderr("Data or instance access missing, cannot continue!");
503         return nullptr;
504     }
505 
506     if (const LV2_DirectAccess_Interface* const directAccess = (const LV2_DirectAccess_Interface*)extData->data_access(DISTRHO_PLUGIN_LV2_STATE_PREFIX "direct-access"))
507         instance = directAccess->get_instance_pointer(instance);
508     else
509         instance = nullptr;
510 
511     if (instance == nullptr)
512     {
513         d_stderr("Failed to get direct access, cannot continue!");
514         return nullptr;
515     }
516 #endif
517 
518     const intptr_t winId = (intptr_t)parentId;
519     float sampleRate = 0.0f;
520     float scaleFactor = 1.0f;
521     uint32_t bgColor = 0;
522     uint32_t fgColor = 0xffffffff;
523 
524     if (options != nullptr)
525     {
526         const LV2_URID uridAtomInt     = uridMap->map(uridMap->handle, LV2_ATOM__Int);
527         const LV2_URID uridAtomFloat   = uridMap->map(uridMap->handle, LV2_ATOM__Float);
528         const LV2_URID uridSampleRate  = uridMap->map(uridMap->handle, LV2_PARAMETERS__sampleRate);
529         const LV2_URID uridBgColor     = uridMap->map(uridMap->handle, LV2_UI__backgroundColor);
530         const LV2_URID uridFgColor     = uridMap->map(uridMap->handle, LV2_UI__foregroundColor);
531         const LV2_URID uridScaleFactor = uridMap->map(uridMap->handle, LV2_UI__scaleFactor);
532 
533         for (int i=0; options[i].key != 0; ++i)
534         {
535             /**/ if (options[i].key == uridSampleRate)
536             {
537                 if (options[i].type == uridAtomFloat)
538                     sampleRate = *(const float*)options[i].value;
539                 else
540                     d_stderr("Host provides UI sample-rate but has wrong value type");
541             }
542             else if (options[i].key == uridScaleFactor)
543             {
544                 if (options[i].type == uridAtomFloat)
545                     scaleFactor = *(const float*)options[i].value;
546                 else
547                     d_stderr("Host provides UI scale factor but has wrong value type");
548             }
549             else if (options[i].key == uridBgColor)
550             {
551                 if (options[i].type == uridAtomInt)
552                     bgColor = (uint32_t)*(const int32_t*)options[i].value;
553                 else
554                     d_stderr("Host provides UI background color but has wrong value type");
555             }
556             else if (options[i].key == uridFgColor)
557             {
558                 if (options[i].type == uridAtomInt)
559                     fgColor = (uint32_t)*(const int32_t*)options[i].value;
560                 else
561                     d_stderr("Host provides UI foreground color but has wrong value type");
562             }
563         }
564     }
565 
566     if (sampleRate < 1.0)
567     {
568         d_stdout("WARNING: this host does not send sample-rate information for LV2 UIs, using 44100 as fallback (this could be wrong)");
569         sampleRate = 44100.0;
570     }
571 
572     return new UiLv2(bundlePath, winId, options, uridMap, features,
573                      controller, writeFunction, widget, instance,
574                      sampleRate, scaleFactor, bgColor, fgColor);
575 }
576 
577 #define uiPtr ((UiLv2*)ui)
578 
lv2ui_cleanup(LV2UI_Handle ui)579 static void lv2ui_cleanup(LV2UI_Handle ui)
580 {
581     delete uiPtr;
582 }
583 
lv2ui_port_event(LV2UI_Handle ui,uint32_t portIndex,uint32_t bufferSize,uint32_t format,const void * buffer)584 static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer)
585 {
586     uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer);
587 }
588 
589 // -----------------------------------------------------------------------
590 
lv2ui_idle(LV2UI_Handle ui)591 static int lv2ui_idle(LV2UI_Handle ui)
592 {
593     return uiPtr->lv2ui_idle();
594 }
595 
lv2ui_show(LV2UI_Handle ui)596 static int lv2ui_show(LV2UI_Handle ui)
597 {
598     return uiPtr->lv2ui_show();
599 }
600 
lv2ui_hide(LV2UI_Handle ui)601 static int lv2ui_hide(LV2UI_Handle ui)
602 {
603     return uiPtr->lv2ui_hide();
604 }
605 
606 // -----------------------------------------------------------------------
607 
lv2_get_options(LV2UI_Handle ui,LV2_Options_Option * options)608 static uint32_t lv2_get_options(LV2UI_Handle ui, LV2_Options_Option* options)
609 {
610     return uiPtr->lv2_get_options(options);
611 }
612 
lv2_set_options(LV2UI_Handle ui,const LV2_Options_Option * options)613 static uint32_t lv2_set_options(LV2UI_Handle ui, const LV2_Options_Option* options)
614 {
615     return uiPtr->lv2_set_options(options);
616 }
617 
618 // -----------------------------------------------------------------------
619 
620 #if DISTRHO_PLUGIN_WANT_PROGRAMS
lv2ui_select_program(LV2UI_Handle ui,uint32_t bank,uint32_t program)621 static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program)
622 {
623     uiPtr->lv2ui_select_program(bank, program);
624 }
625 #endif
626 
627 // -----------------------------------------------------------------------
628 
lv2ui_extension_data(const char * uri)629 static const void* lv2ui_extension_data(const char* uri)
630 {
631     static const LV2_Options_Interface options = { lv2_get_options, lv2_set_options };
632     static const LV2UI_Idle_Interface  uiIdle  = { lv2ui_idle };
633     static const LV2UI_Show_Interface  uiShow  = { lv2ui_show, lv2ui_hide };
634 
635     if (std::strcmp(uri, LV2_OPTIONS__interface) == 0)
636         return &options;
637     if (std::strcmp(uri, LV2_UI__idleInterface) == 0)
638         return &uiIdle;
639     if (std::strcmp(uri, LV2_UI__showInterface) == 0)
640         return &uiShow;
641 
642 #if DISTRHO_PLUGIN_WANT_PROGRAMS
643     static const LV2_Programs_UI_Interface uiPrograms = { lv2ui_select_program };
644 
645     if (std::strcmp(uri, LV2_PROGRAMS__UIInterface) == 0)
646         return &uiPrograms;
647 #endif
648 
649     return nullptr;
650 }
651 
652 #undef instancePtr
653 
654 // -----------------------------------------------------------------------
655 
656 static const LV2UI_Descriptor sLv2UiDescriptor = {
657     DISTRHO_UI_URI,
658     lv2ui_instantiate,
659     lv2ui_cleanup,
660     lv2ui_port_event,
661     lv2ui_extension_data
662 };
663 
664 // -----------------------------------------------------------------------
665 
666 END_NAMESPACE_DISTRHO
667 
668 DISTRHO_PLUGIN_EXPORT
lv2ui_descriptor(uint32_t index)669 const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index)
670 {
671     USE_NAMESPACE_DISTRHO
672     return (index == 0) ? &sLv2UiDescriptor : nullptr;
673 }
674 
675 // -----------------------------------------------------------------------
676