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.

496 lines
14KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2022 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. #ifndef DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
  17. #define DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED
  18. #include "../DistrhoUI.hpp"
  19. #ifdef DISTRHO_PLUGIN_TARGET_VST3
  20. # include "DistrhoPluginVST.hpp"
  21. #endif
  22. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  23. # include "../extra/Sleep.hpp"
  24. // TODO import and use file browser here
  25. #else
  26. # include "../../dgl/src/ApplicationPrivateData.hpp"
  27. # include "../../dgl/src/WindowPrivateData.hpp"
  28. # include "../../dgl/src/pugl.hpp"
  29. #endif
  30. #if defined(DISTRHO_PLUGIN_TARGET_JACK) || defined(DISTRHO_PLUGIN_TARGET_DSSI)
  31. # define DISTRHO_UI_IS_STANDALONE 1
  32. #else
  33. # define DISTRHO_UI_IS_STANDALONE 0
  34. #endif
  35. #if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP)
  36. # define DISTRHO_UI_USES_SIZE_REQUEST true
  37. #else
  38. # define DISTRHO_UI_USES_SIZE_REQUEST false
  39. #endif
  40. #ifdef DISTRHO_PLUGIN_TARGET_VST2
  41. # undef DISTRHO_UI_USER_RESIZABLE
  42. # define DISTRHO_UI_USER_RESIZABLE 0
  43. #endif
  44. START_NAMESPACE_DISTRHO
  45. // -----------------------------------------------------------------------
  46. // Plugin Application, will set class name based on plugin details
  47. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  48. struct PluginApplication
  49. {
  50. DGL_NAMESPACE::IdleCallback* idleCallback;
  51. UI* ui;
  52. explicit PluginApplication()
  53. : idleCallback(nullptr),
  54. ui(nullptr) {}
  55. void addIdleCallback(DGL_NAMESPACE::IdleCallback* const cb)
  56. {
  57. DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,);
  58. DISTRHO_SAFE_ASSERT_RETURN(idleCallback == nullptr,);
  59. idleCallback = cb;
  60. }
  61. bool isQuitting() const noexcept
  62. {
  63. return ui->isQuitting();
  64. }
  65. bool isStandalone() const noexcept
  66. {
  67. return DISTRHO_UI_IS_STANDALONE;
  68. }
  69. void exec()
  70. {
  71. while (ui->isRunning())
  72. {
  73. d_msleep(30);
  74. idleCallback->idleCallback();
  75. }
  76. if (! ui->isQuitting())
  77. ui->close();
  78. }
  79. // these are not needed
  80. void idle() {}
  81. void quit() {}
  82. void triggerIdleCallbacks() {}
  83. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
  84. };
  85. #else
  86. class PluginApplication : public DGL_NAMESPACE::Application
  87. {
  88. public:
  89. explicit PluginApplication()
  90. : DGL_NAMESPACE::Application(DISTRHO_UI_IS_STANDALONE)
  91. {
  92. #ifndef DISTRHO_OS_WASM
  93. const char* const className = (
  94. #ifdef DISTRHO_PLUGIN_BRAND
  95. DISTRHO_PLUGIN_BRAND
  96. #else
  97. DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
  98. #endif
  99. "-" DISTRHO_PLUGIN_NAME
  100. );
  101. setClassName(className);
  102. #endif
  103. }
  104. void triggerIdleCallbacks()
  105. {
  106. pData->triggerIdleCallbacks();
  107. }
  108. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication)
  109. };
  110. #endif
  111. // -----------------------------------------------------------------------
  112. // Plugin Window, will pass some Window events to UI
  113. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  114. class PluginWindow
  115. {
  116. UI* const ui;
  117. public:
  118. explicit PluginWindow(UI* const uiPtr, PluginApplication& app)
  119. : ui(uiPtr)
  120. {
  121. app.ui = ui;
  122. }
  123. // fetch cached data
  124. uint getWidth() const noexcept { return ui->pData.width; }
  125. uint getHeight() const noexcept { return ui->pData.height; }
  126. double getScaleFactor() const noexcept { return ui->pData.scaleFactor; }
  127. // direct mappings
  128. void close() { ui->close(); }
  129. void focus() { ui->focus(); }
  130. void show() { ui->show(); }
  131. bool isResizable() const noexcept { return ui->isResizable(); }
  132. bool isVisible() const noexcept { return ui->isVisible(); }
  133. void setTitle(const char* const title) { ui->setTitle(title); }
  134. void setVisible(const bool visible) { ui->setVisible(visible); }
  135. uintptr_t getNativeWindowHandle() const noexcept { return ui->getNativeWindowHandle(); }
  136. void getGeometryConstraints(uint& minimumWidth, uint& minimumHeight, bool& keepAspectRatio) const noexcept
  137. {
  138. minimumWidth = ui->pData.minWidth;
  139. minimumHeight = ui->pData.minHeight;
  140. keepAspectRatio = ui->pData.keepAspectRatio;
  141. }
  142. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
  143. };
  144. #else // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  145. class PluginWindow : public DGL_NAMESPACE::Window
  146. {
  147. UI* const ui;
  148. bool initializing;
  149. bool receivedReshapeDuringInit;
  150. public:
  151. explicit PluginWindow(UI* const uiPtr,
  152. PluginApplication& app,
  153. const uintptr_t parentWindowHandle,
  154. const uint width,
  155. const uint height,
  156. const double scaleFactor)
  157. : Window(app, parentWindowHandle, width, height, scaleFactor,
  158. DISTRHO_UI_USER_RESIZABLE, DISTRHO_UI_USES_SIZE_REQUEST, false),
  159. ui(uiPtr),
  160. initializing(true),
  161. receivedReshapeDuringInit(false)
  162. {
  163. if (pData->view == nullptr)
  164. return;
  165. // this is called just before creating UI, ensuring proper context to it
  166. if (pData->initPost())
  167. puglBackendEnter(pData->view);
  168. }
  169. ~PluginWindow() override
  170. {
  171. if (pData->view != nullptr)
  172. puglBackendLeave(pData->view);
  173. }
  174. // called after creating UI, restoring proper context
  175. void leaveContext()
  176. {
  177. if (pData->view == nullptr)
  178. return;
  179. if (receivedReshapeDuringInit)
  180. ui->uiReshape(getWidth(), getHeight());
  181. initializing = false;
  182. puglBackendLeave(pData->view);
  183. }
  184. // used for temporary windows (VST/CLAP get size without active/visible view)
  185. void setIgnoreIdleCallbacks(const bool ignore = true)
  186. {
  187. pData->ignoreIdleCallbacks = ignore;
  188. }
  189. // called right before deleting UI, ensuring correct context
  190. void enterContextForDeletion()
  191. {
  192. if (pData->view != nullptr)
  193. puglBackendEnter(pData->view);
  194. }
  195. #if defined(DISTRHO_PLUGIN_TARGET_VST3) || defined(DISTRHO_PLUGIN_TARGET_CLAP)
  196. void setSizeFromHost(const uint width, const uint height)
  197. {
  198. puglSetSizeAndDefault(pData->view, width, height);
  199. }
  200. #endif
  201. std::vector<DGL_NAMESPACE::ClipboardDataOffer> getClipboardDataOfferTypes()
  202. {
  203. return Window::getClipboardDataOfferTypes();
  204. }
  205. protected:
  206. uint32_t onClipboardDataOffer() override
  207. {
  208. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr, 0);
  209. if (initializing)
  210. return 0;
  211. return ui->uiClipboardDataOffer();
  212. }
  213. void onFocus(const bool focus, const DGL_NAMESPACE::CrossingMode mode) override
  214. {
  215. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  216. if (initializing)
  217. return;
  218. ui->uiFocus(focus, mode);
  219. }
  220. void onReshape(const uint width, const uint height) override
  221. {
  222. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  223. if (initializing)
  224. {
  225. receivedReshapeDuringInit = true;
  226. return;
  227. }
  228. ui->uiReshape(width, height);
  229. }
  230. void onScaleFactorChanged(const double scaleFactor) override
  231. {
  232. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  233. if (initializing)
  234. return;
  235. ui->uiScaleFactorChanged(scaleFactor);
  236. }
  237. # if DISTRHO_UI_FILE_BROWSER
  238. void onFileSelected(const char* filename) override;
  239. # endif
  240. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow)
  241. };
  242. #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  243. // -----------------------------------------------------------------------
  244. // UI callbacks
  245. typedef void (*editParamFunc) (void* ptr, uint32_t rindex, bool started);
  246. typedef void (*setParamFunc) (void* ptr, uint32_t rindex, float value);
  247. typedef void (*setStateFunc) (void* ptr, const char* key, const char* value);
  248. typedef void (*sendNoteFunc) (void* ptr, uint8_t channel, uint8_t note, uint8_t velo);
  249. typedef void (*setSizeFunc) (void* ptr, uint width, uint height);
  250. typedef bool (*fileRequestFunc) (void* ptr, const char* key);
  251. // -----------------------------------------------------------------------
  252. // UI private data
  253. struct UI::PrivateData {
  254. // DGL
  255. PluginApplication app;
  256. ScopedPointer<PluginWindow> window;
  257. // DSP
  258. double sampleRate;
  259. uint32_t parameterOffset;
  260. void* dspPtr;
  261. // UI
  262. uint bgColor;
  263. uint fgColor;
  264. double scaleFactor;
  265. uintptr_t winId;
  266. #if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  267. char* uiStateFileKeyRequest;
  268. #endif
  269. char* bundlePath;
  270. // Ignore initial resize events while initializing
  271. bool initializing;
  272. // Callbacks
  273. void* callbacksPtr;
  274. editParamFunc editParamCallbackFunc;
  275. setParamFunc setParamCallbackFunc;
  276. setStateFunc setStateCallbackFunc;
  277. sendNoteFunc sendNoteCallbackFunc;
  278. setSizeFunc setSizeCallbackFunc;
  279. fileRequestFunc fileRequestCallbackFunc;
  280. PrivateData() noexcept
  281. : app(),
  282. window(nullptr),
  283. sampleRate(0),
  284. parameterOffset(0),
  285. dspPtr(nullptr),
  286. bgColor(0),
  287. fgColor(0xffffffff),
  288. scaleFactor(1.0),
  289. winId(0),
  290. #if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  291. uiStateFileKeyRequest(nullptr),
  292. #endif
  293. bundlePath(nullptr),
  294. initializing(true),
  295. callbacksPtr(nullptr),
  296. editParamCallbackFunc(nullptr),
  297. setParamCallbackFunc(nullptr),
  298. setStateCallbackFunc(nullptr),
  299. sendNoteCallbackFunc(nullptr),
  300. setSizeCallbackFunc(nullptr),
  301. fileRequestCallbackFunc(nullptr)
  302. {
  303. #if defined(DISTRHO_PLUGIN_TARGET_DSSI) || defined(DISTRHO_PLUGIN_TARGET_LV2)
  304. parameterOffset += DISTRHO_PLUGIN_NUM_INPUTS + DISTRHO_PLUGIN_NUM_OUTPUTS;
  305. # if DISTRHO_PLUGIN_WANT_LATENCY
  306. parameterOffset += 1;
  307. # endif
  308. #endif
  309. #ifdef DISTRHO_PLUGIN_TARGET_LV2
  310. # if (DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS || DISTRHO_PLUGIN_WANT_STATE)
  311. parameterOffset += 1;
  312. # endif
  313. # if (DISTRHO_PLUGIN_WANT_MIDI_OUTPUT || DISTRHO_PLUGIN_WANT_STATE)
  314. parameterOffset += 1;
  315. # endif
  316. #endif
  317. #ifdef DISTRHO_PLUGIN_TARGET_VST3
  318. parameterOffset += kVst3InternalParameterCount;
  319. #endif
  320. }
  321. ~PrivateData() noexcept
  322. {
  323. #if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  324. std::free(uiStateFileKeyRequest);
  325. #endif
  326. std::free(bundlePath);
  327. }
  328. void editParamCallback(const uint32_t rindex, const bool started)
  329. {
  330. if (editParamCallbackFunc != nullptr)
  331. editParamCallbackFunc(callbacksPtr, rindex, started);
  332. }
  333. void setParamCallback(const uint32_t rindex, const float value)
  334. {
  335. if (setParamCallbackFunc != nullptr)
  336. setParamCallbackFunc(callbacksPtr, rindex, value);
  337. }
  338. void setStateCallback(const char* const key, const char* const value)
  339. {
  340. if (setStateCallbackFunc != nullptr)
  341. setStateCallbackFunc(callbacksPtr, key, value);
  342. }
  343. void sendNoteCallback(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  344. {
  345. if (sendNoteCallbackFunc != nullptr)
  346. sendNoteCallbackFunc(callbacksPtr, channel, note, velocity);
  347. }
  348. void setSizeCallback(const uint width, const uint height)
  349. {
  350. if (setSizeCallbackFunc != nullptr)
  351. setSizeCallbackFunc(callbacksPtr, width, height);
  352. }
  353. // implemented below, after PluginWindow
  354. bool fileRequestCallback(const char* const key);
  355. static UI::PrivateData* s_nextPrivateData;
  356. #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  357. static ExternalWindow::PrivateData createNextWindow(UI* ui, uint width, uint height, bool adjustForScaleFactor);
  358. #else
  359. static PluginWindow& createNextWindow(UI* ui, uint width, uint height, bool adjustForScaleFactor);
  360. #endif
  361. };
  362. // -----------------------------------------------------------------------
  363. // UI private data fileRequestCallback, which requires PluginWindow definitions
  364. inline bool UI::PrivateData::fileRequestCallback(const char* const key)
  365. {
  366. if (fileRequestCallbackFunc != nullptr)
  367. return fileRequestCallbackFunc(callbacksPtr, key);
  368. #if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  369. std::free(uiStateFileKeyRequest);
  370. uiStateFileKeyRequest = strdup(key);
  371. DISTRHO_SAFE_ASSERT_RETURN(uiStateFileKeyRequest != nullptr, false);
  372. char title[0xff];
  373. snprintf(title, sizeof(title)-1u, DISTRHO_PLUGIN_NAME ": %s", key);
  374. title[sizeof(title)-1u] = '\0';
  375. DGL_NAMESPACE::FileBrowserOptions opts;
  376. opts.title = title;
  377. return window->openFileBrowser(opts);
  378. #endif
  379. return false;
  380. }
  381. // -----------------------------------------------------------------------
  382. // PluginWindow onFileSelected that require UI::PrivateData definitions
  383. #if DISTRHO_UI_FILE_BROWSER && !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  384. inline void PluginWindow::onFileSelected(const char* const filename)
  385. {
  386. DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
  387. if (initializing)
  388. return;
  389. #if DISTRHO_PLUGIN_WANT_STATE
  390. if (char* const key = ui->uiData->uiStateFileKeyRequest)
  391. {
  392. ui->uiData->uiStateFileKeyRequest = nullptr;
  393. if (filename != nullptr)
  394. {
  395. // notify DSP
  396. ui->setState(key, filename);
  397. // notify UI
  398. ui->stateChanged(key, filename);
  399. }
  400. std::free(key);
  401. return;
  402. }
  403. #endif
  404. puglBackendEnter(pData->view);
  405. ui->uiFileBrowserSelected(filename);
  406. puglBackendLeave(pData->view);
  407. }
  408. #endif
  409. // -----------------------------------------------------------------------
  410. END_NAMESPACE_DISTRHO
  411. #endif // DISTRHO_UI_PRIVATE_DATA_HPP_INCLUDED