DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

678 lines
22KB

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