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.

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