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.

837 lines
26KB

  1. /*
  2. * DISTRHO Ildaeil Plugin
  3. * Copyright (C) 2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the LICENSE file.
  16. */
  17. #include "CarlaNativePlugin.h"
  18. #include "DistrhoUI.hpp"
  19. #include "DistrhoPlugin.hpp"
  20. #include "PluginHostWindow.hpp"
  21. #include "extra/Thread.hpp"
  22. // IDE helper
  23. #include "DearImGui.hpp"
  24. #include <vector>
  25. START_NAMESPACE_DISTRHO
  26. // --------------------------------------------------------------------------------------------------------------------
  27. class IldaeilPlugin : public Plugin
  28. {
  29. public:
  30. const NativePluginDescriptor* fCarlaPluginDescriptor;
  31. NativePluginHandle fCarlaPluginHandle;
  32. NativeHostDescriptor fCarlaHostDescriptor;
  33. CarlaHostHandle fCarlaHostHandle;
  34. void* fUI;
  35. // ...
  36. };
  37. // --------------------------------------------------------------------------------------------------------------------
  38. void ildaeilParameterChangeForUI(void* ui, uint32_t index, float value);
  39. const char* ildaeilOpenFileForUI(void* ui, bool isDir, const char* title, const char* filter);
  40. // --------------------------------------------------------------------------------------------------------------------
  41. using namespace CarlaBackend;
  42. // shared resource pointer
  43. // carla_juce_init();
  44. class IldaeilUI : public UI,
  45. public Thread,
  46. public PluginHostWindow::Callbacks
  47. {
  48. static constexpr const uint kInitialWidth = 1220;
  49. static constexpr const uint kInitialHeight = 640;
  50. static constexpr const uint kGenericWidth = 300;
  51. static constexpr const uint kGenericHeight = 400;
  52. static constexpr const uint kExtraHeight = 35;
  53. enum {
  54. kDrawingInit,
  55. kDrawingError,
  56. kDrawingLoading,
  57. kDrawingPluginList,
  58. kDrawingPluginEmbedUI,
  59. kDrawingPluginGenericUI,
  60. kDrawingPluginPendingFromInit
  61. } fDrawingState;
  62. struct PluginInfoCache {
  63. char* name;
  64. char* label;
  65. PluginInfoCache()
  66. : name(nullptr),
  67. label(nullptr) {}
  68. ~PluginInfoCache()
  69. {
  70. std::free(name);
  71. std::free(label);
  72. }
  73. };
  74. struct PluginGenericUI {
  75. char* title;
  76. uint parameterCount;
  77. struct Parameter {
  78. char* name;
  79. char* format;
  80. uint32_t rindex;
  81. bool boolean, bvalue;
  82. float min, max;
  83. Parameter()
  84. : name(nullptr),
  85. format(nullptr),
  86. rindex(0),
  87. boolean(false),
  88. bvalue(false),
  89. min(0.0f),
  90. max(1.0f) {}
  91. ~Parameter()
  92. {
  93. std::free(name);
  94. std::free(format);
  95. }
  96. }* parameters;
  97. float* values;
  98. PluginGenericUI()
  99. : title(nullptr),
  100. parameterCount(0),
  101. parameters(nullptr),
  102. values(nullptr) {}
  103. ~PluginGenericUI()
  104. {
  105. std::free(title);
  106. delete[] parameters;
  107. delete[] values;
  108. }
  109. };
  110. IldaeilPlugin* const fPlugin;
  111. PluginHostWindow fPluginHostWindow;
  112. PluginType fPluginType;
  113. uint fPluginCount;
  114. uint fPluginSelected;
  115. bool fPluginScanningFinished;
  116. bool fPluginHasCustomUI;
  117. bool fPluginHasEmbedUI;
  118. bool fPluginWillRunInBridgeMode;
  119. PluginInfoCache* fPlugins;
  120. ScopedPointer<PluginGenericUI> fPluginGenericUI;
  121. bool fPluginSearchActive;
  122. char fPluginSearchString[0xff];
  123. public:
  124. IldaeilUI()
  125. : UI(kInitialWidth, kInitialHeight),
  126. Thread("IldaeilScanner"),
  127. fDrawingState(kDrawingInit),
  128. fPlugin((IldaeilPlugin*)getPluginInstancePointer()),
  129. fPluginHostWindow(getWindow(), this),
  130. fPluginType(PLUGIN_LV2),
  131. fPluginCount(0),
  132. fPluginSelected(0),
  133. fPluginScanningFinished(false),
  134. fPluginHasCustomUI(false),
  135. fPluginHasEmbedUI(false),
  136. fPluginWillRunInBridgeMode(false),
  137. fPlugins(nullptr),
  138. fPluginSearchActive(false)
  139. {
  140. if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr)
  141. {
  142. fDrawingState = kDrawingError;
  143. return;
  144. }
  145. std::strcpy(fPluginSearchString, "Search...");
  146. const double scaleFactor = getScaleFactor();
  147. if (d_isNotEqual(scaleFactor, 1.0))
  148. {
  149. setSize(kInitialWidth * scaleFactor, kInitialHeight * scaleFactor);
  150. fPluginHostWindow.setPositionAndSize(0, kExtraHeight * scaleFactor,
  151. kInitialWidth * scaleFactor, (kInitialHeight - kExtraHeight) * scaleFactor);
  152. }
  153. else
  154. {
  155. fPluginHostWindow.setPositionAndSize(0, kExtraHeight, kInitialWidth, kInitialHeight-kExtraHeight);
  156. }
  157. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  158. char winIdStr[24];
  159. std::snprintf(winIdStr, sizeof(winIdStr), "%lx", (ulong)getWindow().getNativeWindowHandle());
  160. carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr);
  161. carla_set_engine_option(handle, ENGINE_OPTION_FRONTEND_UI_SCALE, getScaleFactor()*1000, nullptr);
  162. if (carla_get_current_plugin_count(handle) != 0)
  163. {
  164. const uint hints = carla_get_plugin_info(handle, 0)->hints;
  165. fDrawingState = kDrawingPluginPendingFromInit;
  166. fPluginHasCustomUI = hints & PLUGIN_HAS_CUSTOM_UI;
  167. fPluginHasEmbedUI = hints & PLUGIN_HAS_CUSTOM_EMBED_UI;
  168. }
  169. fPlugin->fUI = this;
  170. }
  171. ~IldaeilUI() override
  172. {
  173. if (fPlugin != nullptr && fPlugin->fCarlaHostHandle != nullptr)
  174. {
  175. fPlugin->fUI = nullptr;
  176. carla_set_engine_option(fPlugin->fCarlaHostHandle, ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0");
  177. }
  178. if (isThreadRunning())
  179. stopThread(-1);
  180. hidePluginUI();
  181. fPluginGenericUI = nullptr;
  182. delete[] fPlugins;
  183. }
  184. void changeParameterFromDSP(const uint32_t index, const float value)
  185. {
  186. if (PluginGenericUI* const ui = fPluginGenericUI)
  187. {
  188. for (uint32_t i=0; i < ui->parameterCount; ++i)
  189. {
  190. if (ui->parameters[i].rindex != index)
  191. continue;
  192. ui->values[i] = value;
  193. if (ui->parameters[i].boolean)
  194. ui->parameters[i].bvalue = value > ui->parameters[i].min;
  195. break;
  196. }
  197. }
  198. }
  199. const char* openFileFromDSP(const bool /*isDir*/, const char* const title, const char* const /*filter*/)
  200. {
  201. Window::FileBrowserOptions opts;
  202. opts.title = title;
  203. getWindow().openFileBrowser(opts);
  204. return nullptr;
  205. }
  206. void showPluginUI(const CarlaHostHandle handle)
  207. {
  208. const CarlaPluginInfo* const info = carla_get_plugin_info(handle, 0);
  209. if (info->hints & PLUGIN_HAS_CUSTOM_EMBED_UI)
  210. {
  211. fDrawingState = kDrawingPluginEmbedUI;
  212. fPluginHasCustomUI = true;
  213. fPluginHasEmbedUI = true;
  214. carla_embed_custom_ui(handle, 0, fPluginHostWindow.attachAndGetWindowHandle());
  215. }
  216. else
  217. {
  218. fDrawingState = kDrawingPluginGenericUI;
  219. fPluginHasCustomUI = info->hints & PLUGIN_HAS_CUSTOM_UI;
  220. fPluginHasEmbedUI = false;
  221. if (fPluginGenericUI == nullptr)
  222. createPluginGenericUI(handle, info);
  223. else
  224. updatePluginGenericUI(handle);
  225. const double scaleFactor = getScaleFactor();
  226. setSize(kGenericWidth * scaleFactor, (kGenericHeight + kExtraHeight) * scaleFactor);
  227. }
  228. repaint();
  229. }
  230. void hidePluginUI()
  231. {
  232. if (fPlugin == nullptr || fPlugin->fCarlaHostHandle == nullptr)
  233. return;
  234. if (fDrawingState == kDrawingPluginGenericUI || fDrawingState == kDrawingPluginEmbedUI)
  235. carla_show_custom_ui(fPlugin->fCarlaHostHandle, 0, false);
  236. fPluginHostWindow.hide();
  237. }
  238. void createPluginGenericUI(const CarlaHostHandle handle, const CarlaPluginInfo* const info)
  239. {
  240. PluginGenericUI* const ui = new PluginGenericUI;
  241. String title(info->name);
  242. title += " by ";
  243. title += info->maker;
  244. ui->title = title.getAndReleaseBuffer();
  245. const uint32_t pcount = ui->parameterCount = carla_get_parameter_count(handle, 0);
  246. // make count of valid parameters
  247. for (uint32_t i=0; i < pcount; ++i)
  248. {
  249. const ParameterData* const pdata = carla_get_parameter_data(handle, 0, i);
  250. if (pdata->type != PARAMETER_INPUT ||
  251. (pdata->hints & PARAMETER_IS_ENABLED) == 0x0 ||
  252. (pdata->hints & PARAMETER_IS_READ_ONLY) != 0x0)
  253. {
  254. --ui->parameterCount;
  255. continue;
  256. }
  257. }
  258. ui->parameters = new PluginGenericUI::Parameter[ui->parameterCount];
  259. ui->values = new float[ui->parameterCount];
  260. // now safely fill in details
  261. for (uint32_t i=0, j=0; i < pcount; ++i)
  262. {
  263. const ParameterData* const pdata = carla_get_parameter_data(handle, 0, i);
  264. if (pdata->type != PARAMETER_INPUT ||
  265. (pdata->hints & PARAMETER_IS_ENABLED) == 0x0 ||
  266. (pdata->hints & PARAMETER_IS_READ_ONLY) != 0x0)
  267. continue;
  268. const CarlaParameterInfo* const pinfo = carla_get_parameter_info(handle, 0, i);
  269. const ::ParameterRanges* const pranges = carla_get_parameter_ranges(handle, 0, i);
  270. String format;
  271. if (pdata->hints & PARAMETER_IS_INTEGER)
  272. format = "%.0f ";
  273. else
  274. format = "%.3f ";
  275. format += pinfo->unit;
  276. PluginGenericUI::Parameter& param(ui->parameters[j]);
  277. param.name = strdup(pinfo->name);
  278. param.format = format.getAndReleaseBuffer();
  279. param.rindex = i;
  280. param.boolean = pdata->hints & PARAMETER_IS_BOOLEAN;
  281. param.min = pranges->min;
  282. param.max = pranges->max;
  283. ui->values[j] = carla_get_current_parameter_value(handle, 0, i);
  284. if (param.boolean)
  285. param.bvalue = ui->values[j] > param.min;
  286. else
  287. param.bvalue = false;
  288. ++j;
  289. }
  290. fPluginGenericUI = ui;
  291. }
  292. void updatePluginGenericUI(const CarlaHostHandle handle)
  293. {
  294. PluginGenericUI* const ui = fPluginGenericUI;
  295. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  296. for (uint32_t i=0; i < ui->parameterCount; ++i)
  297. {
  298. ui->values[i] = carla_get_current_parameter_value(handle, 0, ui->parameters[i].rindex);
  299. if (ui->parameters[i].boolean)
  300. ui->parameters[i].bvalue = ui->values[i] > ui->parameters[i].min;
  301. }
  302. }
  303. bool loadPlugin(const CarlaHostHandle handle, const char* const label)
  304. {
  305. if (carla_get_current_plugin_count(handle) != 0)
  306. {
  307. hidePluginUI();
  308. carla_replace_plugin(handle, 0);
  309. }
  310. carla_set_engine_option(handle, ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, fPluginWillRunInBridgeMode, nullptr);
  311. if (carla_add_plugin(handle, BINARY_NATIVE, fPluginType, nullptr, nullptr,
  312. label, 0, 0x0, PLUGIN_OPTIONS_NULL))
  313. {
  314. fPluginGenericUI = nullptr;
  315. showPluginUI(handle);
  316. return true;
  317. }
  318. return false;
  319. }
  320. protected:
  321. void pluginWindowResized(uint width, uint height) override
  322. {
  323. setSize(width, height + kExtraHeight * getScaleFactor());
  324. }
  325. void uiIdle() override
  326. {
  327. switch (fDrawingState)
  328. {
  329. case kDrawingInit:
  330. fDrawingState = kDrawingLoading;
  331. startThread();
  332. repaint();
  333. break;
  334. case kDrawingPluginPendingFromInit:
  335. showPluginUI(fPlugin->fCarlaHostHandle);
  336. startThread();
  337. break;
  338. case kDrawingPluginEmbedUI:
  339. fPlugin->fCarlaPluginDescriptor->ui_idle(fPlugin->fCarlaPluginHandle);
  340. fPluginHostWindow.idle();
  341. break;
  342. case kDrawingPluginGenericUI:
  343. fPlugin->fCarlaPluginDescriptor->ui_idle(fPlugin->fCarlaPluginHandle);
  344. break;
  345. default:
  346. break;
  347. }
  348. }
  349. void uiFileBrowserSelected(const char* const filename) override
  350. {
  351. if (fPlugin != nullptr && fPlugin->fCarlaHostHandle != nullptr && filename != nullptr)
  352. carla_set_custom_data(fPlugin->fCarlaHostHandle, 0, CUSTOM_DATA_TYPE_STRING, "file", filename);
  353. }
  354. void run() override
  355. {
  356. if (const uint count = carla_get_cached_plugin_count(fPluginType, nullptr))
  357. {
  358. fPluginCount = 0;
  359. fPlugins = new PluginInfoCache[count];
  360. if (fDrawingState == kDrawingLoading)
  361. fDrawingState = kDrawingPluginList;
  362. for (uint i=0, j; i < count && ! shouldThreadExit(); ++i)
  363. {
  364. const CarlaCachedPluginInfo* const info = carla_get_cached_plugin_info(fPluginType, i);
  365. DISTRHO_SAFE_ASSERT_CONTINUE(info != nullptr);
  366. if (! info->valid)
  367. continue;
  368. #if DISTRHO_PLUGIN_IS_SYNTH
  369. if (info->midiIns != 1 || info->audioOuts != 2)
  370. continue;
  371. if ((info->hints & PLUGIN_IS_SYNTH) == 0x0)
  372. continue;
  373. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  374. if (info->midiIns != 1 || info->midiOuts != 1)
  375. continue;
  376. if (info->audioIns != 0 || info->audioOuts != 0)
  377. continue;
  378. #else
  379. if (info->audioIns != 2 || info->audioOuts != 2)
  380. continue;
  381. #endif
  382. j = fPluginCount;
  383. fPlugins[j].name = strdup(info->name);
  384. fPlugins[j].label = strdup(info->label);
  385. ++fPluginCount;
  386. }
  387. }
  388. if (! shouldThreadExit())
  389. fPluginScanningFinished = true;
  390. }
  391. void onImGuiDisplay() override
  392. {
  393. switch (fDrawingState)
  394. {
  395. case kDrawingInit:
  396. case kDrawingLoading:
  397. case kDrawingPluginPendingFromInit:
  398. drawLoading();
  399. break;
  400. case kDrawingPluginList:
  401. drawPluginList();
  402. break;
  403. case kDrawingError:
  404. // TODO display error message
  405. break;
  406. case kDrawingPluginGenericUI:
  407. drawGenericUI();
  408. // fall-through
  409. case kDrawingPluginEmbedUI:
  410. drawTopBar();
  411. break;
  412. }
  413. }
  414. void drawTopBar()
  415. {
  416. ImGui::SetNextWindowPos(ImVec2(0, 0));
  417. ImGui::SetNextWindowSize(ImVec2(getWidth(), kExtraHeight * getScaleFactor()));
  418. if (ImGui::Begin("Current Plugin", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  419. {
  420. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  421. if (ImGui::Button("Pick Another..."))
  422. {
  423. hidePluginUI();
  424. fDrawingState = kDrawingPluginList;
  425. const double scaleFactor = getScaleFactor();
  426. setSize(kInitialWidth * scaleFactor, kInitialHeight * scaleFactor);
  427. }
  428. ImGui::SameLine();
  429. if (ImGui::Button("Reset"))
  430. {
  431. loadPlugin(handle, carla_get_plugin_info(handle, 0)->label);
  432. }
  433. if (fDrawingState == kDrawingPluginGenericUI && fPluginHasCustomUI)
  434. {
  435. ImGui::SameLine();
  436. if (ImGui::Button("Show Custom GUI"))
  437. {
  438. if (fPluginHasEmbedUI)
  439. {
  440. fDrawingState = kDrawingPluginEmbedUI;
  441. carla_embed_custom_ui(handle, 0, fPluginHostWindow.attachAndGetWindowHandle());
  442. }
  443. else
  444. {
  445. carla_show_custom_ui(handle, 0, true);
  446. }
  447. }
  448. }
  449. if (fDrawingState == kDrawingPluginEmbedUI)
  450. {
  451. ImGui::SameLine();
  452. if (ImGui::Button("Show Generic GUI"))
  453. {
  454. hidePluginUI();
  455. fDrawingState = kDrawingPluginGenericUI;
  456. if (fPluginGenericUI == nullptr)
  457. createPluginGenericUI(handle, carla_get_plugin_info(handle, 0));
  458. else
  459. updatePluginGenericUI(handle);
  460. const double scaleFactor = getScaleFactor();
  461. setSize(std::max(getWidth(), static_cast<uint>(kGenericWidth * scaleFactor + 0.5)),
  462. (kGenericHeight + kExtraHeight) * scaleFactor);
  463. }
  464. }
  465. }
  466. ImGui::End();
  467. }
  468. void setupMainWindowPos()
  469. {
  470. float y = 0;
  471. float height = getHeight();
  472. if (fDrawingState == kDrawingPluginGenericUI)
  473. {
  474. y = (kExtraHeight - 1) * getScaleFactor();
  475. height -= y;
  476. }
  477. ImGui::SetNextWindowPos(ImVec2(0, y));
  478. ImGui::SetNextWindowSize(ImVec2(getWidth(), height));
  479. }
  480. void drawGenericUI()
  481. {
  482. setupMainWindowPos();
  483. PluginGenericUI* const ui = fPluginGenericUI;
  484. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  485. if (ImGui::Begin(ui->title, nullptr, ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoCollapse))
  486. {
  487. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  488. for (uint32_t i=0; i < ui->parameterCount; ++i)
  489. {
  490. PluginGenericUI::Parameter& param(ui->parameters[i]);
  491. if (param.boolean)
  492. {
  493. if (ImGui::Checkbox(param.name, &ui->parameters[i].bvalue))
  494. {
  495. if (ImGui::IsItemActivated())
  496. {
  497. carla_set_parameter_touch(handle, 0, param.rindex, true);
  498. // editParameter(0, true);
  499. }
  500. ui->values[i] = ui->parameters[i].bvalue ? ui->parameters[i].max : ui->parameters[i].min;
  501. carla_set_parameter_value(handle, 0, param.rindex, ui->values[i]);
  502. // setParameterValue(0, ui->values[i]);
  503. }
  504. }
  505. else
  506. {
  507. if (ImGui::SliderFloat(param.name, &ui->values[i], param.min, param.max, param.format))
  508. {
  509. if (ImGui::IsItemActivated())
  510. {
  511. carla_set_parameter_touch(handle, 0, param.rindex, true);
  512. // editParameter(0, true);
  513. }
  514. carla_set_parameter_value(handle, 0, param.rindex, ui->values[i]);
  515. // setParameterValue(0, ui->values[i]);
  516. }
  517. }
  518. if (ImGui::IsItemDeactivated())
  519. {
  520. carla_set_parameter_touch(handle, 0, param.rindex, false);
  521. // editParameter(0, false);
  522. }
  523. }
  524. }
  525. ImGui::End();
  526. }
  527. void drawLoading()
  528. {
  529. setupMainWindowPos();
  530. if (ImGui::Begin("Plugin List", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  531. {
  532. ImGui::TextUnformatted("Loading...", nullptr);
  533. }
  534. ImGui::End();
  535. }
  536. void drawPluginList()
  537. {
  538. setupMainWindowPos();
  539. const CarlaHostHandle handle = fPlugin->fCarlaHostHandle;
  540. if (ImGui::Begin("Plugin List", nullptr, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize))
  541. {
  542. if (ImGui::InputText("", fPluginSearchString, sizeof(fPluginSearchString)-1, ImGuiInputTextFlags_CharsNoBlank|ImGuiInputTextFlags_AutoSelectAll))
  543. fPluginSearchActive = true;
  544. ImGui::BeginDisabled(!fPluginScanningFinished);
  545. if (ImGui::Button("Load Plugin"))
  546. {
  547. do {
  548. const PluginInfoCache& info(fPlugins[fPluginSelected]);
  549. const char* label = nullptr;
  550. switch (fPluginType)
  551. {
  552. case PLUGIN_INTERNAL:
  553. label = info.label;
  554. break;
  555. case PLUGIN_LV2: {
  556. const char* const slash = std::strchr(info.label, DISTRHO_OS_SEP);
  557. DISTRHO_SAFE_ASSERT_BREAK(slash != nullptr);
  558. label = slash+1;
  559. break;
  560. }
  561. default:
  562. break;
  563. }
  564. DISTRHO_SAFE_ASSERT_BREAK(label != nullptr);
  565. d_stdout("Loading %s...", info.name);
  566. if (loadPlugin(handle, label))
  567. {
  568. ImGui::EndDisabled();
  569. ImGui::End();
  570. return;
  571. }
  572. } while (false);
  573. }
  574. ImGui::SameLine();
  575. ImGui::Checkbox("Run in bridge mode", &fPluginWillRunInBridgeMode);
  576. if (carla_get_current_plugin_count(handle) != 0)
  577. {
  578. ImGui::SameLine();
  579. if (ImGui::Button("Cancel"))
  580. {
  581. showPluginUI(handle);
  582. }
  583. }
  584. ImGui::EndDisabled();
  585. if (ImGui::BeginChild("pluginlistwindow"))
  586. {
  587. if (ImGui::BeginTable("pluginlist",
  588. fPluginType == PLUGIN_LV2 ? 3 : 2,
  589. ImGuiTableFlags_NoSavedSettings|ImGuiTableFlags_NoClip))
  590. {
  591. const char* const search = fPluginSearchActive && fPluginSearchString[0] != '\0' ? fPluginSearchString : nullptr;
  592. switch (fPluginType)
  593. {
  594. case PLUGIN_INTERNAL:
  595. ImGui::TableSetupColumn("Name");
  596. ImGui::TableSetupColumn("Label");
  597. ImGui::TableHeadersRow();
  598. break;
  599. case PLUGIN_LV2:
  600. ImGui::TableSetupColumn("Name");
  601. ImGui::TableSetupColumn("Bundle");
  602. ImGui::TableSetupColumn("URI");
  603. ImGui::TableHeadersRow();
  604. break;
  605. default:
  606. break;
  607. }
  608. for (uint i=0; i<fPluginCount; ++i)
  609. {
  610. const PluginInfoCache& info(fPlugins[i]);
  611. if (search != nullptr && strcasestr(info.name, search) == nullptr)
  612. continue;
  613. bool selected = fPluginSelected == i;
  614. switch (fPluginType)
  615. {
  616. case PLUGIN_INTERNAL:
  617. ImGui::TableNextRow();
  618. ImGui::TableSetColumnIndex(0);
  619. ImGui::Selectable(info.name, &selected);
  620. ImGui::TableSetColumnIndex(1);
  621. ImGui::Selectable(info.label, &selected);
  622. break;
  623. case PLUGIN_LV2: {
  624. const char* const slash = std::strchr(info.label, DISTRHO_OS_SEP);
  625. DISTRHO_SAFE_ASSERT_CONTINUE(slash != nullptr);
  626. ImGui::TableNextRow();
  627. ImGui::TableSetColumnIndex(0);
  628. ImGui::Selectable(info.name, &selected);
  629. ImGui::TableSetColumnIndex(1);
  630. ImGui::Selectable(slash+1, &selected);
  631. ImGui::TableSetColumnIndex(2);
  632. ImGui::TextUnformatted(info.label, slash);
  633. break;
  634. }
  635. default:
  636. break;
  637. }
  638. if (selected)
  639. fPluginSelected = i;
  640. }
  641. ImGui::EndTable();
  642. }
  643. ImGui::EndChild();
  644. }
  645. }
  646. ImGui::End();
  647. }
  648. protected:
  649. /* --------------------------------------------------------------------------------------------------------
  650. * DSP/Plugin Callbacks */
  651. void parameterChanged(uint32_t, float) override
  652. {
  653. }
  654. void stateChanged(const char* const key, const char* const) override
  655. {
  656. if (std::strcmp(key, "project") == 0)
  657. hidePluginUI();
  658. }
  659. // -------------------------------------------------------------------------------------------------------
  660. private:
  661. /**
  662. Set our UI class as non-copyable and add a leak detector just in case.
  663. */
  664. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilUI)
  665. };
  666. // --------------------------------------------------------------------------------------------------------------------
  667. void ildaeilParameterChangeForUI(void* const ui, const uint32_t index, const float value)
  668. {
  669. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  670. static_cast<IldaeilUI*>(ui)->changeParameterFromDSP(index, value);
  671. }
  672. const char* ildaeilOpenFileForUI(void* const ui, const bool isDir, const char* const title, const char* const filter)
  673. {
  674. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, nullptr);
  675. return static_cast<IldaeilUI*>(ui)->openFileFromDSP(isDir, title, filter);
  676. }
  677. /* --------------------------------------------------------------------------------------------------------------------
  678. * UI entry point, called by DPF to create a new UI instance. */
  679. UI* createUI()
  680. {
  681. return new IldaeilUI();
  682. }
  683. // --------------------------------------------------------------------------------------------------------------------
  684. END_NAMESPACE_DISTRHO