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.

992 lines
35KB

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