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.

1189 lines
40KB

  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 "travesty/edit_controller.h"
  18. #include "travesty/host.h"
  19. #include "travesty/view.h"
  20. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  21. # include <atomic>
  22. #else
  23. // quick and dirty std::atomic replacement for the things we need
  24. namespace std {
  25. struct atomic_int {
  26. volatile int value;
  27. explicit atomic_int(volatile int v) noexcept : value(v) {}
  28. int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); }
  29. int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); }
  30. operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); }
  31. };
  32. };
  33. #endif
  34. /* TODO items:
  35. * - mousewheel event
  36. * - key down/up events
  37. * - size constraints
  38. * - host-side resize
  39. * - oddities with init and size callback (triggered too early?)
  40. */
  41. #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
  42. # define DPF_VST3_USING_HOST_RUN_LOOP
  43. #endif
  44. #ifndef DPF_VST3_TIMER_INTERVAL
  45. # define DPF_VST3_TIMER_INTERVAL 16 /* ~60 fps */
  46. #endif
  47. START_NAMESPACE_DISTRHO
  48. // --------------------------------------------------------------------------------------------------------------------
  49. #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
  50. static constexpr const sendNoteFunc sendNoteCallback = nullptr;
  51. #endif
  52. #if ! DISTRHO_PLUGIN_WANT_STATE
  53. static constexpr const setStateFunc setStateCallback = nullptr;
  54. #endif
  55. // --------------------------------------------------------------------------------------------------------------------
  56. // Utility functions (defined on plugin side)
  57. const char* tuid2str(const v3_tuid iid);
  58. void strncpy_utf16(int16_t* dst, const char* src, size_t length);
  59. struct ScopedUTF16String {
  60. int16_t* str;
  61. ScopedUTF16String(const char* const s) noexcept;
  62. ~ScopedUTF16String() noexcept;
  63. operator int16_t*() const noexcept;
  64. };
  65. // --------------------------------------------------------------------------------------------------------------------
  66. /**
  67. * VST3 UI class.
  68. *
  69. * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things.
  70. * The UI is created during the "attach" view event, and destroyed during "removed".
  71. *
  72. * Note that DPF VST3 implementation works over the connection point interface,
  73. * rather than using edit controller directly.
  74. * This allows the UI to be running remotely from the DSP.
  75. *
  76. * The low-level VST3 stuff comes after.
  77. */
  78. class UIVst3
  79. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  80. : public IdleCallback
  81. #endif
  82. {
  83. public:
  84. UIVst3(v3_plugin_view** const view,
  85. v3_host_application** const host,
  86. const intptr_t winId,
  87. const float scaleFactor,
  88. const double sampleRate,
  89. void* const instancePointer)
  90. : fUI(this, winId, sampleRate,
  91. editParameterCallback,
  92. setParameterCallback,
  93. setStateCallback,
  94. sendNoteCallback,
  95. setSizeCallback,
  96. nullptr, // TODO file request
  97. nullptr, // bundlePath
  98. instancePointer,
  99. scaleFactor),
  100. fView(view),
  101. fHostContext(host),
  102. fConnection(nullptr),
  103. fFrame(nullptr),
  104. fReadyForPluginData(false),
  105. fScaleFactor(scaleFactor)
  106. {
  107. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  108. fUI.addIdleCallbackForVST3(this, DPF_VST3_TIMER_INTERVAL);
  109. #endif
  110. }
  111. ~UIVst3()
  112. {
  113. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  114. fUI.removeIdleCallbackForVST3(this);
  115. #endif
  116. if (fConnection != nullptr)
  117. disconnect();
  118. }
  119. // ----------------------------------------------------------------------------------------------------------------
  120. // v3_plugin_view interface calls
  121. v3_result onWheel(float /*distance*/)
  122. {
  123. // TODO
  124. return V3_NOT_IMPLEMENTED;
  125. }
  126. v3_result onKeyDown(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/)
  127. {
  128. // TODO
  129. return V3_NOT_IMPLEMENTED;
  130. }
  131. v3_result onKeyUp(int16_t /*key_char*/, int16_t /*key_code*/, int16_t /*modifiers*/)
  132. {
  133. // TODO
  134. return V3_NOT_IMPLEMENTED;
  135. }
  136. v3_result getSize(v3_view_rect* const rect) const noexcept
  137. {
  138. std::memset(rect, 0, sizeof(v3_view_rect));
  139. rect->right = fUI.getWidth();
  140. rect->bottom = fUI.getHeight();
  141. #ifdef DISTRHO_OS_MAC
  142. const double scaleFactor = fUI.getScaleFactor();
  143. rect->right /= scaleFactor;
  144. rect->bottom /= scaleFactor;
  145. #endif
  146. return V3_OK;
  147. }
  148. v3_result onSize(v3_view_rect* const /*rect*/)
  149. {
  150. // TODO
  151. return V3_NOT_IMPLEMENTED;
  152. }
  153. v3_result onFocus(const bool state)
  154. {
  155. #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  156. fUI.notifyFocusChanged(state);
  157. return V3_OK;
  158. #else
  159. return V3_NOT_IMPLEMENTED;
  160. // unused
  161. (void)state;
  162. #endif
  163. }
  164. v3_result setFrame(v3_plugin_frame** const frame) noexcept
  165. {
  166. fFrame = frame;
  167. return V3_OK;
  168. }
  169. v3_result checkSizeConstraint(v3_view_rect* const /*rect*/)
  170. {
  171. // TODO
  172. return V3_NOT_IMPLEMENTED;
  173. }
  174. // ----------------------------------------------------------------------------------------------------------------
  175. // v3_connection_point interface calls
  176. void connect(v3_connection_point** const point) noexcept
  177. {
  178. DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,);
  179. fConnection = point;
  180. d_stdout("requesting current plugin state");
  181. v3_message** const message = createMessage("init");
  182. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  183. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  184. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  185. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  186. v3_cpp_obj(fConnection)->notify(fConnection, message);
  187. v3_cpp_obj_unref(attrlist);
  188. v3_cpp_obj_unref(message);
  189. }
  190. void disconnect() noexcept
  191. {
  192. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  193. d_stdout("reporting UI closed");
  194. fReadyForPluginData = false;
  195. v3_message** const message = createMessage("close");
  196. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  197. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  198. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  199. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  200. v3_cpp_obj(fConnection)->notify(fConnection, message);
  201. v3_cpp_obj_unref(attrlist);
  202. v3_cpp_obj_unref(message);
  203. fConnection = nullptr;
  204. }
  205. v3_result notify(v3_message** const message)
  206. {
  207. const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
  208. DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
  209. v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
  210. DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
  211. if (std::strcmp(msgid, "ready") == 0)
  212. {
  213. DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR);
  214. fReadyForPluginData = true;
  215. return V3_OK;
  216. }
  217. if (std::strcmp(msgid, "parameter-set") == 0)
  218. {
  219. int64_t rindex;
  220. double value;
  221. v3_result res;
  222. res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
  223. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  224. res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value);
  225. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  226. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  227. if (rindex == 0)
  228. {
  229. DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INTERNAL_ERR);
  230. fUI.programLoaded(static_cast<uint32_t>(value + 0.5));
  231. }
  232. else
  233. #endif
  234. {
  235. rindex -= fUI.getParameterOffset();
  236. DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INTERNAL_ERR);
  237. fUI.parameterChanged(static_cast<uint32_t>(rindex), value);
  238. }
  239. return V3_OK;
  240. }
  241. #if DISTRHO_PLUGIN_WANT_STATE
  242. if (std::strcmp(msgid, "state-set") == 0)
  243. {
  244. int16_t* key16;
  245. int16_t* value16;
  246. uint32_t keySize, valueSize;
  247. v3_result res;
  248. res = v3_cpp_obj(attrs)->get_binary(attrs, "key", (const void**)&key16, &keySize);
  249. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  250. res = v3_cpp_obj(attrs)->get_binary(attrs, "value", (const void**)&value16, &valueSize);
  251. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  252. // do cheap inline conversion
  253. char* const key = (char*)key16;
  254. char* const value = (char*)value16;
  255. for (uint32_t i=0; i<keySize/sizeof(int16_t); ++i)
  256. key[i] = key16[i];
  257. for (uint32_t i=0; i<valueSize/sizeof(int16_t); ++i)
  258. value[i] = value16[i];
  259. fUI.stateChanged(key, value);
  260. return V3_OK;
  261. }
  262. #endif
  263. if (std::strcmp(msgid, "sample-rate") == 0)
  264. {
  265. double sampleRate;
  266. v3_result res;
  267. res = v3_cpp_obj(attrs)->get_float(attrs, "value", &sampleRate);
  268. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  269. DISTRHO_SAFE_ASSERT_RETURN(sampleRate > 0, V3_INVALID_ARG);
  270. fUI.setSampleRate(sampleRate, true);
  271. return V3_OK;
  272. }
  273. d_stdout("UIVst3 received unknown msg '%s'", msgid);
  274. return V3_NOT_IMPLEMENTED;
  275. }
  276. // ----------------------------------------------------------------------------------------------------------------
  277. // v3_plugin_view_content_scale_steinberg interface calls
  278. v3_result setContentScaleFactor(const float factor)
  279. {
  280. if (d_isEqual(fScaleFactor, factor))
  281. return V3_OK;
  282. fScaleFactor = factor;
  283. fUI.notifyScaleFactorChanged(factor);
  284. return V3_OK;
  285. }
  286. // ----------------------------------------------------------------------------------------------------------------
  287. // special idle callback on macOS and Windows
  288. #if defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)
  289. void idleCallback() override
  290. {
  291. if (fReadyForPluginData)
  292. {
  293. fReadyForPluginData = false;
  294. requestMorePluginData();
  295. }
  296. fUI.idleForVST3();
  297. }
  298. #else
  299. // ----------------------------------------------------------------------------------------------------------------
  300. // v3_timer_handler interface calls
  301. void onTimer()
  302. {
  303. if (fReadyForPluginData)
  304. {
  305. fReadyForPluginData = false;
  306. requestMorePluginData();
  307. }
  308. fUI.plugin_idle();
  309. }
  310. #endif
  311. // ----------------------------------------------------------------------------------------------------------------
  312. private:
  313. // Plugin UI
  314. UIExporter fUI;
  315. // VST3 stuff
  316. v3_plugin_view** const fView;
  317. v3_host_application** const fHostContext;
  318. v3_connection_point** fConnection;
  319. v3_plugin_frame** fFrame;
  320. // Temporary data
  321. bool fReadyForPluginData;
  322. float fScaleFactor;
  323. // ----------------------------------------------------------------------------------------------------------------
  324. // helper functions called during message passing
  325. v3_message** createMessage(const char* const id) const
  326. {
  327. DISTRHO_SAFE_ASSERT_RETURN(fHostContext != nullptr, nullptr);
  328. v3_tuid iid;
  329. memcpy(iid, v3_message_iid, sizeof(v3_tuid));
  330. v3_message** msg = nullptr;
  331. const v3_result res = v3_cpp_obj(fHostContext)->create_instance(fHostContext, iid, iid, (void**)&msg);
  332. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
  333. DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);
  334. v3_cpp_obj(msg)->set_message_id(msg, id);
  335. return msg;
  336. }
  337. void requestMorePluginData() const
  338. {
  339. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  340. v3_message** const message = createMessage("idle");
  341. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  342. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  343. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  344. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  345. v3_cpp_obj(fConnection)->notify(fConnection, message);
  346. v3_cpp_obj_unref(attrlist);
  347. v3_cpp_obj_unref(message);
  348. }
  349. // ----------------------------------------------------------------------------------------------------------------
  350. // DPF callbacks
  351. void editParameter(const uint32_t rindex, const bool started) const
  352. {
  353. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  354. v3_message** const message = createMessage("parameter-edit");
  355. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  356. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  357. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  358. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  359. v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
  360. v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0);
  361. v3_cpp_obj(fConnection)->notify(fConnection, message);
  362. v3_cpp_obj_unref(attrlist);
  363. v3_cpp_obj_unref(message);
  364. }
  365. static void editParameterCallback(void* ptr, uint32_t rindex, bool started)
  366. {
  367. ((UIVst3*)ptr)->editParameter(rindex, started);
  368. }
  369. void setParameterValue(const uint32_t rindex, const float realValue)
  370. {
  371. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  372. v3_message** const message = createMessage("parameter-set");
  373. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  374. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  375. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  376. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  377. v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
  378. v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue);
  379. v3_cpp_obj(fConnection)->notify(fConnection, message);
  380. v3_cpp_obj_unref(attrlist);
  381. v3_cpp_obj_unref(message);
  382. }
  383. static void setParameterCallback(void* ptr, uint32_t rindex, float value)
  384. {
  385. ((UIVst3*)ptr)->setParameterValue(rindex, value);
  386. }
  387. void setSize(uint width, uint height)
  388. {
  389. DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
  390. DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,);
  391. d_stdout("from UI setSize %u %u | %p %p", width, height, fView, fFrame);
  392. #ifdef DISTRHO_OS_MAC
  393. const double scaleFactor = fUI.getScaleFactor();
  394. width /= scaleFactor;
  395. height /= scaleFactor;
  396. #endif
  397. v3_view_rect rect;
  398. std::memset(&rect, 0, sizeof(rect));
  399. rect.right = width;
  400. rect.bottom = height;
  401. v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect);
  402. }
  403. static void setSizeCallback(void* ptr, uint width, uint height)
  404. {
  405. ((UIVst3*)ptr)->setSize(width, height);
  406. }
  407. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  408. void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
  409. {
  410. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  411. v3_message** const message = createMessage("midi");
  412. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  413. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  414. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  415. uint8_t midiData[3];
  416. midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel;
  417. midiData[1] = note;
  418. midiData[2] = velocity;
  419. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  420. v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData));
  421. v3_cpp_obj(fConnection)->notify(fConnection, message);
  422. v3_cpp_obj_unref(attrlist);
  423. v3_cpp_obj_unref(message);
  424. }
  425. static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
  426. {
  427. ((UIVst3*)ptr)->sendNote(channel, note, velocity);
  428. }
  429. #endif
  430. #if DISTRHO_PLUGIN_WANT_STATE
  431. void setState(const char* const key, const char* const value)
  432. {
  433. DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
  434. v3_message** const message = createMessage("state-set");
  435. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  436. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  437. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  438. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
  439. v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key));
  440. v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value));
  441. v3_cpp_obj(fConnection)->notify(fConnection, message);
  442. v3_cpp_obj_unref(attrlist);
  443. v3_cpp_obj_unref(message);
  444. }
  445. static void setStateCallback(void* ptr, const char* key, const char* value)
  446. {
  447. ((UIVst3*)ptr)->setState(key, value);
  448. }
  449. #endif
  450. };
  451. // --------------------------------------------------------------------------------------------------------------------
  452. /**
  453. * VST3 low-level pointer thingies follow, proceed with care.
  454. */
  455. // --------------------------------------------------------------------------------------------------------------------
  456. // v3_funknown for classes with a single instance
  457. template<class T>
  458. static V3_API uint32_t dpf_single_instance_ref(void* self)
  459. {
  460. return ++(*(T**)self)->refcounter;
  461. }
  462. template<class T>
  463. static V3_API uint32_t dpf_single_instance_unref(void* self)
  464. {
  465. return --(*(T**)self)->refcounter;
  466. }
  467. // --------------------------------------------------------------------------------------------------------------------
  468. // dpf_connection_point
  469. struct dpf_connection_point : v3_connection_point_cpp {
  470. std::atomic_int refcounter;
  471. ScopedPointer<UIVst3>& uivst3;
  472. v3_connection_point** other;
  473. dpf_connection_point(ScopedPointer<UIVst3>& v)
  474. : refcounter(1),
  475. uivst3(v),
  476. other(nullptr)
  477. {
  478. // v3_funknown, single instance
  479. query_interface = query_interface_connection_point;
  480. ref = dpf_single_instance_ref<dpf_connection_point>;
  481. unref = dpf_single_instance_unref<dpf_connection_point>;
  482. // v3_connection_point
  483. point.connect = connect;
  484. point.disconnect = disconnect;
  485. point.notify = notify;
  486. }
  487. // ----------------------------------------------------------------------------------------------------------------
  488. // v3_funknown
  489. static V3_API v3_result query_interface_connection_point(void* self, const v3_tuid iid, void** iface)
  490. {
  491. if (v3_tuid_match(iid, v3_funknown_iid))
  492. {
  493. *iface = self;
  494. return V3_OK;
  495. }
  496. if (v3_tuid_match(iid, v3_connection_point_iid))
  497. {
  498. *iface = self;
  499. return V3_OK;
  500. }
  501. *iface = NULL;
  502. return V3_NO_INTERFACE;
  503. }
  504. // ----------------------------------------------------------------------------------------------------------------
  505. // v3_connection_point
  506. static V3_API v3_result connect(void* self, v3_connection_point** other)
  507. {
  508. d_stdout("dpf_connection_point::connect => %p %p", self, other);
  509. dpf_connection_point* const point = *(dpf_connection_point**)self;
  510. DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED);
  511. DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
  512. point->other = other;
  513. if (UIVst3* const uivst3 = point->uivst3)
  514. uivst3->connect(other);
  515. return V3_OK;
  516. };
  517. static V3_API v3_result disconnect(void* self, v3_connection_point** other)
  518. {
  519. d_stdout("dpf_connection_point::disconnect => %p %p", self, other);
  520. dpf_connection_point* const point = *(dpf_connection_point**)self;
  521. DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED);
  522. DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
  523. point->other = nullptr;
  524. if (UIVst3* const uivst3 = point->uivst3)
  525. uivst3->disconnect();
  526. return V3_OK;
  527. };
  528. static V3_API v3_result notify(void* self, v3_message** message)
  529. {
  530. dpf_connection_point* const point = *(dpf_connection_point**)self;
  531. DISTRHO_SAFE_ASSERT_RETURN(point != nullptr, V3_NOT_INITIALIZED);
  532. UIVst3* const uivst3 = point->uivst3;
  533. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  534. return uivst3->notify(message);
  535. }
  536. };
  537. // --------------------------------------------------------------------------------------------------------------------
  538. // dpf_plugin_view_content_scale
  539. struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp {
  540. std::atomic_int refcounter;
  541. ScopedPointer<UIVst3>& uivst3;
  542. // cached values
  543. float scaleFactor;
  544. dpf_plugin_view_content_scale(ScopedPointer<UIVst3>& v)
  545. : refcounter(1),
  546. uivst3(v),
  547. scaleFactor(0.0f)
  548. {
  549. // v3_funknown, single instance
  550. query_interface = query_interface_view_content_scale;
  551. ref = dpf_single_instance_ref<dpf_plugin_view_content_scale>;
  552. unref = dpf_single_instance_unref<dpf_plugin_view_content_scale>;
  553. // v3_plugin_view_content_scale
  554. scale.set_content_scale_factor = set_content_scale_factor;
  555. }
  556. // ----------------------------------------------------------------------------------------------------------------
  557. // v3_funknown
  558. static V3_API v3_result query_interface_view_content_scale(void* self, const v3_tuid iid, void** iface)
  559. {
  560. if (v3_tuid_match(iid, v3_funknown_iid))
  561. {
  562. *iface = self;
  563. return V3_OK;
  564. }
  565. if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid))
  566. {
  567. *iface = self;
  568. return V3_OK;
  569. }
  570. *iface = NULL;
  571. return V3_NO_INTERFACE;
  572. }
  573. // ----------------------------------------------------------------------------------------------------------------
  574. // v3_plugin_view_content_scale
  575. static V3_API v3_result set_content_scale_factor(void* self, float factor)
  576. {
  577. d_stdout("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor);
  578. dpf_plugin_view_content_scale* const scale = *(dpf_plugin_view_content_scale**)self;
  579. DISTRHO_SAFE_ASSERT_RETURN(scale != nullptr, V3_NOT_INITIALIZED);
  580. scale->scaleFactor = factor;
  581. if (UIVst3* const uivst3 = scale->uivst3)
  582. return uivst3->setContentScaleFactor(factor);
  583. return V3_NOT_INITIALIZED;
  584. }
  585. };
  586. #ifdef DPF_VST3_USING_HOST_RUN_LOOP
  587. // --------------------------------------------------------------------------------------------------------------------
  588. // dpf_timer_handler
  589. struct dpf_timer_handler : v3_timer_handler_cpp {
  590. std::atomic_int refcounter;
  591. ScopedPointer<UIVst3>& uivst3;
  592. bool valid;
  593. dpf_timer_handler(ScopedPointer<UIVst3>& v)
  594. : refcounter(1),
  595. uivst3(v),
  596. valid(true)
  597. {
  598. // v3_funknown, single instance
  599. query_interface = query_interface_timer_handler;
  600. ref = dpf_single_instance_ref<dpf_timer_handler>;
  601. unref = dpf_single_instance_unref<dpf_timer_handler>;
  602. // v3_timer_handler
  603. handler.on_timer = on_timer;
  604. }
  605. // ----------------------------------------------------------------------------------------------------------------
  606. // v3_funknown
  607. static V3_API v3_result query_interface_timer_handler(void* self, const v3_tuid iid, void** iface)
  608. {
  609. if (v3_tuid_match(iid, v3_funknown_iid))
  610. {
  611. *iface = self;
  612. return V3_OK;
  613. }
  614. if (v3_tuid_match(iid, v3_timer_handler_iid))
  615. {
  616. *iface = self;
  617. return V3_OK;
  618. }
  619. *iface = NULL;
  620. return V3_NO_INTERFACE;
  621. }
  622. // ----------------------------------------------------------------------------------------------------------------
  623. // v3_timer_handler
  624. static V3_API void on_timer(void* self)
  625. {
  626. dpf_timer_handler* const handler = *(dpf_timer_handler**)self;
  627. DISTRHO_SAFE_ASSERT_RETURN(handler != nullptr,);
  628. DISTRHO_SAFE_ASSERT_RETURN(handler->valid,);
  629. handler->uivst3->onTimer();
  630. }
  631. };
  632. #endif
  633. // --------------------------------------------------------------------------------------------------------------------
  634. // dpf_plugin_view
  635. static const char* const kSupportedPlatforms[] = {
  636. #ifdef _WIN32
  637. V3_VIEW_PLATFORM_TYPE_HWND,
  638. #elif defined(__APPLE__)
  639. V3_VIEW_PLATFORM_TYPE_NSVIEW,
  640. #else
  641. V3_VIEW_PLATFORM_TYPE_X11,
  642. #endif
  643. };
  644. struct dpf_plugin_view : v3_plugin_view_cpp {
  645. std::atomic_int refcounter;
  646. dpf_plugin_view** const self;
  647. ScopedPointer<dpf_connection_point> connection;
  648. ScopedPointer<dpf_plugin_view_content_scale> scale;
  649. #ifdef DPF_VST3_USING_HOST_RUN_LOOP
  650. ScopedPointer<dpf_timer_handler> timer;
  651. #endif
  652. ScopedPointer<UIVst3> uivst3;
  653. // cached values
  654. v3_host_application** const host;
  655. void* const instancePointer;
  656. double sampleRate;
  657. v3_plugin_frame** frame;
  658. dpf_plugin_view(dpf_plugin_view** const s, v3_host_application** const h, void* const instance, const double sr)
  659. : refcounter(1),
  660. self(s),
  661. host(h),
  662. instancePointer(instance),
  663. sampleRate(sr),
  664. frame(nullptr)
  665. {
  666. // v3_funknown, everything custom
  667. query_interface = query_interface_view;
  668. ref = ref_view;
  669. unref = unref_view;
  670. // v3_plugin_view
  671. view.is_platform_type_supported = is_platform_type_supported;
  672. view.attached = attached;
  673. view.removed = removed;
  674. view.on_wheel = on_wheel;
  675. view.on_key_down = on_key_down;
  676. view.on_key_up = on_key_up;
  677. view.get_size = get_size;
  678. view.on_size = on_size;
  679. view.on_focus = on_focus;
  680. view.set_frame = set_frame;
  681. view.can_resize = can_resize;
  682. view.check_size_constraint = check_size_constraint;
  683. }
  684. // ----------------------------------------------------------------------------------------------------------------
  685. // v3_funknown
  686. static V3_API v3_result query_interface_view(void* self, const v3_tuid iid, void** iface)
  687. {
  688. d_stdout("dpf_plugin_view::query_interface => %p %s %p", self, tuid2str(iid), iface);
  689. *iface = NULL;
  690. DISTRHO_SAFE_ASSERT_RETURN(self != nullptr, V3_NO_INTERFACE);
  691. if (v3_tuid_match(iid, v3_funknown_iid))
  692. {
  693. *iface = self;
  694. return V3_OK;
  695. }
  696. if (v3_tuid_match(iid, v3_plugin_view_iid))
  697. {
  698. *iface = self;
  699. return V3_OK;
  700. }
  701. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  702. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NO_INTERFACE);
  703. if (v3_tuid_match(v3_connection_point_iid, iid))
  704. {
  705. if (view->connection == nullptr)
  706. view->connection = new dpf_connection_point(view->uivst3);
  707. else
  708. ++view->connection->refcounter;
  709. *iface = &view->connection;
  710. return V3_OK;
  711. }
  712. if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid))
  713. {
  714. if (view->scale == nullptr)
  715. view->scale = new dpf_plugin_view_content_scale(view->uivst3);
  716. else
  717. ++view->scale->refcounter;
  718. *iface = &view->scale;
  719. return V3_OK;
  720. }
  721. return V3_NO_INTERFACE;
  722. }
  723. static V3_API uint32_t ref_view(void* self)
  724. {
  725. return ++(*(dpf_plugin_view**)self)->refcounter;
  726. }
  727. static V3_API uint32_t unref_view(void* self)
  728. {
  729. dpf_plugin_view** const viewptr = (dpf_plugin_view**)self;
  730. dpf_plugin_view* const view = *viewptr;
  731. if (const int refcount = --view->refcounter)
  732. {
  733. d_stdout("dpf_plugin_view::unref => %p | refcount %i", self, refcount);
  734. return refcount;
  735. }
  736. d_stdout("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self);
  737. DISTRHO_SAFE_ASSERT_RETURN(viewptr == view->self, V3_INTERNAL_ERR);
  738. if (view->connection != nullptr && view->connection->other)
  739. v3_cpp_obj(view->connection->other)->disconnect(view->connection->other,
  740. (v3_connection_point**)&view->connection);
  741. if (dpf_connection_point* const conn = view->connection)
  742. {
  743. if (const int refcount = conn->refcounter)
  744. {
  745. d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount);
  746. return V3_INVALID_ARG;
  747. }
  748. }
  749. if (dpf_plugin_view_content_scale* const scale = view->scale)
  750. {
  751. if (const int refcount = scale->refcounter)
  752. {
  753. d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount);
  754. return V3_INVALID_ARG;
  755. }
  756. }
  757. delete view;
  758. delete viewptr;
  759. return 0;
  760. }
  761. // ----------------------------------------------------------------------------------------------------------------
  762. // v3_plugin_view
  763. static V3_API v3_result is_platform_type_supported(void* self, const char* platform_type)
  764. {
  765. d_stdout("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type);
  766. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  767. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  768. for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i)
  769. {
  770. if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0)
  771. return V3_OK;
  772. }
  773. return V3_NOT_IMPLEMENTED;
  774. }
  775. static V3_API v3_result attached(void* self, void* parent, const char* platform_type)
  776. {
  777. d_stdout("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type);
  778. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  779. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  780. DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG);
  781. for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i)
  782. {
  783. if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0)
  784. {
  785. #ifdef DPF_VST3_USING_HOST_RUN_LOOP
  786. // find host run loop to plug ourselves into (required on some systems)
  787. DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG);
  788. v3_run_loop** runloop = nullptr;
  789. v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop);
  790. DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG);
  791. #endif
  792. const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f;
  793. view->uivst3 = new UIVst3((v3_plugin_view**)view->self,
  794. view->host,
  795. (uintptr_t)parent,
  796. scaleFactor,
  797. view->sampleRate,
  798. view->instancePointer);
  799. if (dpf_connection_point* const point = view->connection)
  800. if (point->other != nullptr)
  801. view->uivst3->connect(point->other);
  802. view->uivst3->setFrame(view->frame);
  803. #ifdef DPF_VST3_USING_HOST_RUN_LOOP
  804. // register a timer host run loop stuff
  805. view->timer = new dpf_timer_handler(view->uivst3);
  806. v3_cpp_obj(runloop)->register_timer(runloop,
  807. (v3_timer_handler**)&view->timer,
  808. DPF_VST3_TIMER_INTERVAL);
  809. #endif
  810. return V3_OK;
  811. }
  812. }
  813. return V3_NOT_IMPLEMENTED;
  814. }
  815. static V3_API v3_result removed(void* self)
  816. {
  817. d_stdout("dpf_plugin_view::removed => %p", self);
  818. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  819. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  820. DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG);
  821. #ifdef DPF_VST3_USING_HOST_RUN_LOOP
  822. // unregister our timer as needed
  823. if (view->timer != nullptr)
  824. {
  825. v3_run_loop** runloop = nullptr;
  826. if (view->frame != nullptr)
  827. v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop);
  828. if (runloop != nullptr)
  829. {
  830. v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer);
  831. if (const int refcount = --view->timer->refcounter)
  832. {
  833. view->timer->valid = false;
  834. d_stderr("VST3 warning: Host run loop did not give away timer (refcount %d)", refcount);
  835. }
  836. else
  837. {
  838. view->timer = nullptr;
  839. }
  840. }
  841. else
  842. {
  843. view->timer->valid = false;
  844. d_stderr("VST3 warning: Host run loop not available during dpf_plugin_view::removed");
  845. }
  846. }
  847. #endif
  848. view->uivst3 = nullptr;
  849. return V3_OK;
  850. }
  851. static V3_API v3_result on_wheel(void* self, float distance)
  852. {
  853. d_stdout("dpf_plugin_view::on_wheel => %p %f", self, distance);
  854. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  855. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  856. UIVst3* const uivst3 = view->uivst3;
  857. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  858. return uivst3->onWheel(distance);
  859. }
  860. static V3_API v3_result on_key_down(void* self, int16_t key_char, int16_t key_code, int16_t modifiers)
  861. {
  862. d_stdout("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers);
  863. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  864. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  865. UIVst3* const uivst3 = view->uivst3;
  866. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  867. return uivst3->onKeyDown(key_char, key_code, modifiers);
  868. }
  869. static V3_API v3_result on_key_up(void* self, int16_t key_char, int16_t key_code, int16_t modifiers)
  870. {
  871. d_stdout("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers);
  872. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  873. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  874. UIVst3* const uivst3 = view->uivst3;
  875. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  876. return uivst3->onKeyUp(key_char, key_code, modifiers);
  877. }
  878. static V3_API v3_result get_size(void* self, v3_view_rect* rect)
  879. {
  880. d_stdout("dpf_plugin_view::get_size => %p", self);
  881. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  882. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  883. if (UIVst3* const uivst3 = view->uivst3)
  884. return uivst3->getSize(rect);
  885. // special case: allow UI to not be attached yet, as a way to get size before window creation
  886. const float scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f;
  887. UIExporter tmpUI(nullptr, 0, view->sampleRate,
  888. nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
  889. view->instancePointer, scaleFactor);
  890. rect->right = tmpUI.getWidth();
  891. rect->bottom = tmpUI.getHeight();
  892. return V3_OK;
  893. }
  894. static V3_API v3_result on_size(void* self, v3_view_rect* rect)
  895. {
  896. d_stdout("dpf_plugin_view::on_size => %p %p", self, rect);
  897. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  898. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  899. UIVst3* const uivst3 = view->uivst3;
  900. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  901. return uivst3->onSize(rect);
  902. }
  903. static V3_API v3_result on_focus(void* self, v3_bool state)
  904. {
  905. d_stdout("dpf_plugin_view::on_focus => %p %u", self, state);
  906. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  907. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  908. UIVst3* const uivst3 = view->uivst3;
  909. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  910. return uivst3->onFocus(state);
  911. }
  912. static V3_API v3_result set_frame(void* self, v3_plugin_frame** frame)
  913. {
  914. d_stdout("dpf_plugin_view::set_frame => %p %p", self, frame);
  915. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  916. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  917. view->frame = frame;
  918. if (UIVst3* const uivst3 = view->uivst3)
  919. return uivst3->setFrame(frame);
  920. return V3_NOT_INITIALIZED;
  921. }
  922. static V3_API v3_result can_resize(void* self)
  923. {
  924. d_stdout("dpf_plugin_view::can_resize => %p", self);
  925. // #if DISTRHO_UI_USER_RESIZABLE
  926. // return V3_OK;
  927. // #else
  928. return V3_NOT_IMPLEMENTED;
  929. // #endif
  930. }
  931. static V3_API v3_result check_size_constraint(void* self, v3_view_rect* rect)
  932. {
  933. d_stdout("dpf_plugin_view::check_size_constraint => %p %p", self, rect);
  934. dpf_plugin_view* const view = *(dpf_plugin_view**)self;
  935. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, V3_NOT_INITIALIZED);
  936. UIVst3* const uivst3 = view->uivst3;
  937. DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
  938. return uivst3->checkSizeConstraint(rect);
  939. }
  940. };
  941. // --------------------------------------------------------------------------------------------------------------------
  942. // dpf_plugin_view_create (called from plugin side)
  943. v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);
  944. v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host,
  945. void* const instancePointer,
  946. const double sampleRate)
  947. {
  948. dpf_plugin_view** const viewptr = new dpf_plugin_view*;
  949. *viewptr = new dpf_plugin_view(viewptr, host, instancePointer, sampleRate);
  950. return (v3_plugin_view**)static_cast<void*>(viewptr);
  951. }
  952. // --------------------------------------------------------------------------------------------------------------------
  953. END_NAMESPACE_DISTRHO