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