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.

863 lines
28KB

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