comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoUIVST3.cpp @ 3:84e66ea83026

DPF-Prymula-audioplugins-0.231015-2
author prymula <prymula76@outlook.com>
date Mon, 16 Oct 2023 21:53:34 +0200
parents
children
comparison
equal deleted inserted replaced
2:cf2cb71d31dd 3:84e66ea83026
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
17 #include "DistrhoUIInternal.hpp"
18
19 #include "travesty/base.h"
20 #include "travesty/edit_controller.h"
21 #include "travesty/host.h"
22 #include "travesty/view.h"
23
24 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
25 # if defined(DISTRHO_OS_MAC)
26 # include <CoreFoundation/CoreFoundation.h>
27 # elif defined(DISTRHO_OS_WINDOWS)
28 # include <winuser.h>
29 # define DPF_VST3_WIN32_TIMER_ID 1
30 # endif
31 #endif
32
33 /* TODO items:
34 * - mousewheel event
35 * - file request?
36 */
37
38 #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS))
39 # define DPF_VST3_USING_HOST_RUN_LOOP 1
40 #else
41 # define DPF_VST3_USING_HOST_RUN_LOOP 0
42 #endif
43
44 #ifndef DPF_VST3_TIMER_INTERVAL
45 # define DPF_VST3_TIMER_INTERVAL 16 /* ~60 fps */
46 #endif
47
48 START_NAMESPACE_DISTRHO
49
50 // --------------------------------------------------------------------------------------------------------------------
51
52 #if ! DISTRHO_PLUGIN_WANT_STATE
53 static constexpr const setStateFunc setStateCallback = nullptr;
54 #endif
55 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT
56 static constexpr const sendNoteFunc sendNoteCallback = nullptr;
57 #endif
58
59 // --------------------------------------------------------------------------------------------------------------------
60 // Static data, see DistrhoPlugin.cpp
61
62 extern const char* d_nextBundlePath;
63
64 // --------------------------------------------------------------------------------------------------------------------
65 // Utility functions (defined on plugin side)
66
67 const char* tuid2str(const v3_tuid iid);
68
69 // --------------------------------------------------------------------------------------------------------------------
70
71 static void applyGeometryConstraints(const uint minimumWidth,
72 const uint minimumHeight,
73 const bool keepAspectRatio,
74 v3_view_rect* const rect)
75 {
76 d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | BEFORE",
77 minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom);
78 const int32_t minWidth = static_cast<int32_t>(minimumWidth);
79 const int32_t minHeight = static_cast<int32_t>(minimumHeight);
80
81 if (keepAspectRatio)
82 {
83 if (rect->right < 1)
84 rect->right = 1;
85 if (rect->bottom < 1)
86 rect->bottom = 1;
87
88 const double ratio = static_cast<double>(minWidth) / static_cast<double>(minHeight);
89 const double reqRatio = static_cast<double>(rect->right) / static_cast<double>(rect->bottom);
90
91 if (d_isNotEqual(ratio, reqRatio))
92 {
93 // fix width
94 if (reqRatio > ratio)
95 rect->right = static_cast<int32_t>(rect->bottom * ratio + 0.5);
96 // fix height
97 else
98 rect->bottom = static_cast<int32_t>(static_cast<double>(rect->right) / ratio + 0.5);
99 }
100 }
101
102 if (minWidth > rect->right)
103 rect->right = minWidth;
104 if (minHeight > rect->bottom)
105 rect->bottom = minHeight;
106
107 d_debug("applyGeometryConstraints %u %u %d {%d,%d,%d,%d} | AFTER",
108 minimumWidth, minimumHeight, keepAspectRatio, rect->top, rect->left, rect->right, rect->bottom);
109 }
110
111 // --------------------------------------------------------------------------------------------------------------------
112
113 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
114 static uint translateVST3Modifiers(const int64_t modifiers) noexcept
115 {
116 using namespace DGL_NAMESPACE;
117
118 uint dglmods = 0;
119 if (modifiers & (1 << 0))
120 dglmods |= kModifierShift;
121 if (modifiers & (1 << 1))
122 dglmods |= kModifierAlt;
123 #ifdef DISTRHO_OS_MAC
124 if (modifiers & (1 << 2))
125 dglmods |= kModifierSuper;
126 if (modifiers & (1 << 3))
127 dglmods |= kModifierControl;
128 #else
129 if (modifiers & (1 << 2))
130 dglmods |= kModifierControl;
131 if (modifiers & (1 << 3))
132 dglmods |= kModifierSuper;
133 #endif
134
135 return dglmods;
136 }
137 #endif
138
139 // --------------------------------------------------------------------------------------------------------------------
140
141 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI && !DPF_VST3_USING_HOST_RUN_LOOP
142 /**
143 * Helper class for getting a native idle timer via native APIs.
144 */
145 class NativeIdleHelper
146 {
147 public:
148 NativeIdleHelper(IdleCallback* const callback)
149 : fCallback(callback),
150 #ifdef DISTRHO_OS_MAC
151 fTimerRef(nullptr)
152 #else
153 fTimerWindow(nullptr),
154 fTimerWindowClassName()
155 #endif
156 {
157 }
158
159 void registerNativeIdleCallback()
160 {
161 #ifdef DISTRHO_OS_MAC
162 constexpr const CFTimeInterval interval = DPF_VST3_TIMER_INTERVAL * 0.0001;
163
164 CFRunLoopTimerContext context = {};
165 context.info = this;
166 fTimerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + interval, interval, 0, 0,
167 platformIdleTimerCallback, &context);
168 DISTRHO_SAFE_ASSERT_RETURN(fTimerRef != nullptr,);
169
170 CFRunLoopAddTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes);
171 #else
172 /*
173 * Create an invisible window to handle a timer.
174 * There is no need for implementing a window proc because DefWindowProc already calls the
175 * callback function when processing WM_TIMER messages.
176 */
177 fTimerWindowClassName = (
178 #ifdef DISTRHO_PLUGIN_BRAND
179 DISTRHO_PLUGIN_BRAND
180 #else
181 DISTRHO_MACRO_AS_STRING(DISTRHO_NAMESPACE)
182 #endif
183 "-" DISTRHO_PLUGIN_NAME "-"
184 );
185
186 char suffix[9];
187 std::snprintf(suffix, sizeof(suffix), "%08x", std::rand());
188 suffix[sizeof(suffix)-1] = '\0';
189 fTimerWindowClassName += suffix;
190
191 WNDCLASSEX cls;
192 ZeroMemory(&cls, sizeof(cls));
193 cls.cbSize = sizeof(WNDCLASSEX);
194 cls.cbWndExtra = sizeof(LONG_PTR);
195 cls.lpszClassName = fTimerWindowClassName.buffer();
196 cls.lpfnWndProc = DefWindowProc;
197 RegisterClassEx(&cls);
198
199 fTimerWindow = CreateWindowEx(0, cls.lpszClassName, "DPF Timer Helper",
200 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr);
201 DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,);
202
203 SetWindowLongPtr(fTimerWindow, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(static_cast<void*>(this)));
204 SetTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID, DPF_VST3_TIMER_INTERVAL,
205 static_cast<TIMERPROC>(platformIdleTimerCallback));
206 #endif
207 }
208
209 void unregisterNativeIdleCallback()
210 {
211 #ifdef DISTRHO_OS_MAC
212 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), fTimerRef, kCFRunLoopCommonModes);
213 CFRelease(fTimerRef);
214 #else
215 DISTRHO_SAFE_ASSERT_RETURN(fTimerWindow != nullptr,);
216 KillTimer(fTimerWindow, DPF_VST3_WIN32_TIMER_ID);
217 DestroyWindow(fTimerWindow);
218 UnregisterClass(fTimerWindowClassName, nullptr);
219 #endif
220 }
221
222 private:
223 IdleCallback* const fCallback;
224
225 #ifdef DISTRHO_OS_MAC
226 CFRunLoopTimerRef fTimerRef;
227
228 static void platformIdleTimerCallback(CFRunLoopTimerRef, void* const info)
229 {
230 static_cast<NativeIdleHelper*>(info)->fCallback->idleCallback();
231 }
232 #else
233 HWND fTimerWindow;
234 String fTimerWindowClassName;
235
236 static void WINAPI platformIdleTimerCallback(const HWND hwnd, UINT, UINT_PTR, DWORD)
237 {
238 reinterpret_cast<NativeIdleHelper*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))->fCallback->idleCallback();
239 }
240 #endif
241 };
242 #endif
243
244 /**
245 * Helper class for getting a native idle timer, either through pugl or via native APIs.
246 */
247 #if !DPF_VST3_USING_HOST_RUN_LOOP
248 class NativeIdleCallback : public IdleCallback
249 {
250 public:
251 NativeIdleCallback(UIExporter& ui)
252 : fCallbackRegistered(false),
253 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
254 fIdleHelper(this)
255 #else
256 fUI(ui)
257 #endif
258 {
259 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
260 // unused
261 (void)ui;
262 #endif
263 }
264
265 void registerNativeIdleCallback()
266 {
267 DISTRHO_SAFE_ASSERT_RETURN(!fCallbackRegistered,);
268 fCallbackRegistered = true;
269
270 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
271 fIdleHelper.registerNativeIdleCallback();
272 #else
273 fUI.addIdleCallbackForNativeIdle(this, DPF_VST3_TIMER_INTERVAL);
274 #endif
275 }
276
277 void unregisterNativeIdleCallback()
278 {
279 DISTRHO_SAFE_ASSERT_RETURN(fCallbackRegistered,);
280 fCallbackRegistered = false;
281
282 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
283 fIdleHelper.unregisterNativeIdleCallback();
284 #else
285 fUI.removeIdleCallbackForNativeIdle(this);
286 #endif
287 }
288
289 private:
290 bool fCallbackRegistered;
291 #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI
292 NativeIdleHelper fIdleHelper;
293 #else
294 UIExporter& fUI;
295 #endif
296 };
297 #endif
298
299 // --------------------------------------------------------------------------------------------------------------------
300
301 /**
302 * VST3 UI class.
303 *
304 * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things.
305 * The UI is created during the "attach" view event, and destroyed during "removed".
306 *
307 * The low-level VST3 stuff comes after.
308 */
309 class UIVst3
310 #if !DPF_VST3_USING_HOST_RUN_LOOP
311 : public NativeIdleCallback
312 #endif
313 {
314 public:
315 UIVst3(v3_plugin_view** const view,
316 v3_host_application** const host,
317 v3_connection_point** const connection,
318 v3_plugin_frame** const frame,
319 const intptr_t winId,
320 const float scaleFactor,
321 const double sampleRate,
322 void* const instancePointer,
323 const bool willResizeFromHost,
324 const bool needsResizeFromPlugin)
325 :
326 #if !DPF_VST3_USING_HOST_RUN_LOOP
327 NativeIdleCallback(fUI),
328 #endif
329 fView(view),
330 fHostApplication(host),
331 fConnection(connection),
332 fFrame(frame),
333 fScaleFactor(scaleFactor),
334 fReadyForPluginData(false),
335 fIsResizingFromPlugin(false),
336 fIsResizingFromHost(willResizeFromHost),
337 fNeedsResizeFromPlugin(needsResizeFromPlugin),
338 fNextPluginRect(),
339 fUI(this, winId, sampleRate,
340 editParameterCallback,
341 setParameterCallback,
342 setStateCallback,
343 sendNoteCallback,
344 setSizeCallback,
345 nullptr, // TODO file request
346 d_nextBundlePath,
347 instancePointer,
348 scaleFactor)
349 {
350 }
351
352 ~UIVst3()
353 {
354 #if !DPF_VST3_USING_HOST_RUN_LOOP
355 unregisterNativeIdleCallback();
356 #endif
357
358 if (fConnection != nullptr)
359 disconnect();
360 }
361
362 void postInit(uint32_t nextWidth, uint32_t nextHeight)
363 {
364 if (fIsResizingFromHost && nextWidth > 0 && nextHeight > 0)
365 {
366 #ifdef DISTRHO_OS_MAC
367 const double scaleFactor = fUI.getScaleFactor();
368 nextWidth *= scaleFactor;
369 nextHeight *= scaleFactor;
370 #endif
371
372 if (fUI.getWidth() != nextWidth || fUI.getHeight() != nextHeight)
373 {
374 d_debug("postInit sets new size as %u %u", nextWidth, nextHeight);
375 fUI.setWindowSizeFromHost(nextWidth, nextHeight);
376 }
377 }
378 else if (fNeedsResizeFromPlugin)
379 {
380 d_debug("postInit forcely sets size from plugin as %u %u", fUI.getWidth(), fUI.getHeight());
381 setSize(fUI.getWidth(), fUI.getHeight());
382 }
383
384 if (fConnection != nullptr)
385 connect(fConnection);
386
387 #if !DPF_VST3_USING_HOST_RUN_LOOP
388 registerNativeIdleCallback();
389 #endif
390 }
391
392 // ----------------------------------------------------------------------------------------------------------------
393 // v3_plugin_view interface calls
394
395 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
396 v3_result onWheel(float /*distance*/)
397 {
398 // TODO
399 return V3_NOT_IMPLEMENTED;
400 }
401
402 v3_result onKeyDown(const int16_t keychar, const int16_t keycode, const int16_t modifiers)
403 {
404 DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE);
405
406 bool special;
407 const uint key = translateVstKeyCode(special, keychar, keycode);
408 d_debug("onKeyDown %d %d %x -> %d %d", keychar, keycode, modifiers, special, key);
409
410 return fUI.handlePluginKeyboardVST(true, special, key,
411 keycode >= 0 ? static_cast<uint>(keycode) : 0,
412 translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE;
413 }
414
415 v3_result onKeyUp(const int16_t keychar, const int16_t keycode, const int16_t modifiers)
416 {
417 DISTRHO_SAFE_ASSERT_INT_RETURN(keychar >= 0 && keychar < 0x7f, keychar, V3_FALSE);
418
419 bool special;
420 const uint key = translateVstKeyCode(special, keychar, keycode);
421 d_debug("onKeyUp %d %d %x -> %d %d", keychar, keycode, modifiers, special, key);
422
423 return fUI.handlePluginKeyboardVST(false, special, key,
424 keycode >= 0 ? static_cast<uint>(keycode) : 0,
425 translateVST3Modifiers(modifiers)) ? V3_TRUE : V3_FALSE;
426 }
427
428 v3_result onFocus(const bool state)
429 {
430 if (state)
431 fUI.focus();
432 fUI.notifyFocusChanged(state);
433 return V3_OK;
434 }
435 #endif
436
437 v3_result getSize(v3_view_rect* const rect) const noexcept
438 {
439 if (fIsResizingFromPlugin)
440 {
441 *rect = fNextPluginRect;
442 }
443 else
444 {
445 rect->left = rect->top = 0;
446 rect->right = fUI.getWidth();
447 rect->bottom = fUI.getHeight();
448 #ifdef DISTRHO_OS_MAC
449 const double scaleFactor = fUI.getScaleFactor();
450 rect->right /= scaleFactor;
451 rect->bottom /= scaleFactor;
452 #endif
453 }
454
455 d_debug("getSize request returning %i %i", rect->right, rect->bottom);
456 return V3_OK;
457 }
458
459 v3_result onSize(v3_view_rect* const orect)
460 {
461 v3_view_rect rect = *orect;
462
463 #ifdef DISTRHO_OS_MAC
464 const double scaleFactor = fUI.getScaleFactor();
465 rect.top *= scaleFactor;
466 rect.left *= scaleFactor;
467 rect.right *= scaleFactor;
468 rect.bottom *= scaleFactor;
469 #endif
470
471 if (fIsResizingFromPlugin)
472 {
473 d_debug("host->plugin onSize request %i %i (plugin resize was active, unsetting now)",
474 rect.right - rect.left, rect.bottom - rect.top);
475 fIsResizingFromPlugin = false;
476 }
477 else
478 {
479 d_debug("host->plugin onSize request %i %i (OK)", rect.right - rect.left, rect.bottom - rect.top);
480 }
481
482 fIsResizingFromHost = true;
483 fUI.setWindowSizeFromHost(rect.right - rect.left, rect.bottom - rect.top);
484 return V3_OK;
485 }
486
487 v3_result setFrame(v3_plugin_frame** const frame) noexcept
488 {
489 fFrame = frame;
490 return V3_OK;
491 }
492
493 v3_result canResize() noexcept
494 {
495 return fUI.isResizable() ? V3_TRUE : V3_FALSE;
496 }
497
498 v3_result checkSizeConstraint(v3_view_rect* const rect)
499 {
500 uint minimumWidth, minimumHeight;
501 bool keepAspectRatio;
502 fUI.getGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio);
503
504 #ifdef DISTRHO_OS_MAC
505 const double scaleFactor = fUI.getScaleFactor();
506 minimumWidth /= scaleFactor;
507 minimumHeight /= scaleFactor;
508 #endif
509
510 applyGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, rect);
511 return V3_TRUE;
512 }
513
514 // ----------------------------------------------------------------------------------------------------------------
515 // v3_connection_point interface calls
516
517 void connect(v3_connection_point** const point) noexcept
518 {
519 DISTRHO_SAFE_ASSERT_RETURN(point != nullptr,);
520
521 fConnection = point;
522
523 d_debug("requesting current plugin state");
524
525 v3_message** const message = createMessage("init");
526 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
527
528 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
529 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
530
531 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
532 v3_cpp_obj(fConnection)->notify(fConnection, message);
533
534 v3_cpp_obj_unref(message);
535 }
536
537 void disconnect() noexcept
538 {
539 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
540
541 d_debug("reporting UI closed");
542 fReadyForPluginData = false;
543
544 v3_message** const message = createMessage("close");
545 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
546
547 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
548 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
549
550 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
551 v3_cpp_obj(fConnection)->notify(fConnection, message);
552
553 v3_cpp_obj_unref(message);
554
555 fConnection = nullptr;
556 }
557
558 v3_result notify(v3_message** const message)
559 {
560 const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
561 DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
562
563 v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
564 DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
565
566 if (std::strcmp(msgid, "ready") == 0)
567 {
568 DISTRHO_SAFE_ASSERT_RETURN(! fReadyForPluginData, V3_INTERNAL_ERR);
569 fReadyForPluginData = true;
570 return V3_OK;
571 }
572
573 if (std::strcmp(msgid, "parameter-set") == 0)
574 {
575 int64_t rindex;
576 double value;
577 v3_result res;
578
579 res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
580 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
581
582 res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value);
583 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
584
585 if (rindex < kVst3InternalParameterBaseCount)
586 {
587 switch (rindex)
588 {
589 #if DPF_VST3_USES_SEPARATE_CONTROLLER
590 case kVst3InternalParameterSampleRate:
591 DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG);
592 fUI.setSampleRate(value, true);
593 break;
594 #endif
595 #if DISTRHO_PLUGIN_WANT_PROGRAMS
596 case kVst3InternalParameterProgram:
597 DISTRHO_SAFE_ASSERT_RETURN(value >= 0.0, V3_INVALID_ARG);
598 fUI.programLoaded(static_cast<uint32_t>(value + 0.5));
599 break;
600 #endif
601 }
602
603 // others like latency and buffer-size do not matter on UI side
604 return V3_OK;
605 }
606
607 DISTRHO_SAFE_ASSERT_UINT2_RETURN(rindex >= kVst3InternalParameterCount, rindex, kVst3InternalParameterCount, V3_INVALID_ARG);
608 const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
609
610 fUI.parameterChanged(index, value);
611 return V3_OK;
612 }
613
614 #if DISTRHO_PLUGIN_WANT_STATE
615 if (std::strcmp(msgid, "state-set") == 0)
616 {
617 int64_t keyLength = -1;
618 int64_t valueLength = -1;
619 v3_result res;
620
621 res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength);
622 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
623 DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR);
624
625 res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength);
626 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
627 DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR);
628
629 int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1));
630 DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM);
631
632 int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1));
633 DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM);
634
635 res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*(keyLength+1));
636 DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, keyLength, res);
637
638 if (valueLength != 0)
639 {
640 res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*(valueLength+1));
641 DISTRHO_SAFE_ASSERT_INT2_RETURN(res == V3_OK, res, valueLength, res);
642 }
643
644 // do cheap inline conversion
645 char* const key = (char*)key16;
646 char* const value = (char*)value16;
647
648 for (int64_t i=0; i<keyLength; ++i)
649 key[i] = key16[i];
650 for (int64_t i=0; i<valueLength; ++i)
651 value[i] = value16[i];
652
653 key[keyLength] = '\0';
654 value[valueLength] = '\0';
655
656 fUI.stateChanged(key, value);
657
658 std::free(key16);
659 std::free(value16);
660 return V3_OK;
661 }
662 #endif
663
664 d_stderr("UIVst3 received unknown msg '%s'", msgid);
665
666 return V3_NOT_IMPLEMENTED;
667 }
668
669 // ----------------------------------------------------------------------------------------------------------------
670 // v3_plugin_view_content_scale_steinberg interface calls
671
672 v3_result setContentScaleFactor(const float factor)
673 {
674 if (d_isEqual(fScaleFactor, factor))
675 return V3_OK;
676
677 fScaleFactor = factor;
678 fUI.notifyScaleFactorChanged(factor);
679 return V3_OK;
680 }
681
682 #if DPF_VST3_USING_HOST_RUN_LOOP
683 // ----------------------------------------------------------------------------------------------------------------
684 // v3_timer_handler interface calls
685
686 void onTimer()
687 {
688 fUI.plugin_idle();
689 doIdleStuff();
690 }
691 #else
692 // ----------------------------------------------------------------------------------------------------------------
693 // special idle callback without v3_timer_handler
694
695 void idleCallback() override
696 {
697 fUI.idleFromNativeIdle();
698 doIdleStuff();
699 }
700 #endif
701
702 void doIdleStuff()
703 {
704 if (fReadyForPluginData)
705 {
706 fReadyForPluginData = false;
707 requestMorePluginData();
708 }
709
710 if (fNeedsResizeFromPlugin)
711 {
712 fNeedsResizeFromPlugin = false;
713 d_debug("first resize forced behaviour is now stopped");
714 }
715
716 if (fIsResizingFromHost)
717 {
718 fIsResizingFromHost = false;
719 d_debug("was resizing from host, now stopped");
720 }
721
722 if (fIsResizingFromPlugin)
723 {
724 fIsResizingFromPlugin = false;
725 d_debug("was resizing from plugin, now stopped");
726 }
727 }
728
729 // ----------------------------------------------------------------------------------------------------------------
730
731 private:
732 // VST3 stuff
733 v3_plugin_view** const fView;
734 v3_host_application** const fHostApplication;
735 v3_connection_point** fConnection;
736 v3_plugin_frame** fFrame;
737
738 // Temporary data
739 float fScaleFactor;
740 bool fReadyForPluginData;
741 bool fIsResizingFromPlugin;
742 bool fIsResizingFromHost;
743 bool fNeedsResizeFromPlugin;
744 v3_view_rect fNextPluginRect; // for when plugin requests a new size
745
746 // Plugin UI (after VST3 stuff so the UI can call into us during its constructor)
747 UIExporter fUI;
748
749 // ----------------------------------------------------------------------------------------------------------------
750 // helper functions called during message passing
751
752 v3_message** createMessage(const char* const id) const
753 {
754 DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr);
755
756 v3_tuid iid;
757 std::memcpy(iid, v3_message_iid, sizeof(v3_tuid));
758 v3_message** msg = nullptr;
759 const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg);
760 DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
761 DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);
762
763 v3_cpp_obj(msg)->set_message_id(msg, id);
764 return msg;
765 }
766
767 void requestMorePluginData() const
768 {
769 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
770
771 v3_message** const message = createMessage("idle");
772 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
773
774 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
775 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
776
777 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
778 v3_cpp_obj(fConnection)->notify(fConnection, message);
779
780 v3_cpp_obj_unref(message);
781 }
782
783 // ----------------------------------------------------------------------------------------------------------------
784 // DPF callbacks
785
786 void editParameter(const uint32_t rindex, const bool started) const
787 {
788 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
789
790 v3_message** const message = createMessage("parameter-edit");
791 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
792
793 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
794 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
795
796 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
797 v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
798 v3_cpp_obj(attrlist)->set_int(attrlist, "started", started ? 1 : 0);
799 v3_cpp_obj(fConnection)->notify(fConnection, message);
800
801 v3_cpp_obj_unref(message);
802 }
803
804 static void editParameterCallback(void* const ptr, const uint32_t rindex, const bool started)
805 {
806 static_cast<UIVst3*>(ptr)->editParameter(rindex, started);
807 }
808
809 void setParameterValue(const uint32_t rindex, const float realValue)
810 {
811 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
812
813 v3_message** const message = createMessage("parameter-set");
814 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
815
816 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
817 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
818
819 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
820 v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
821 v3_cpp_obj(attrlist)->set_float(attrlist, "value", realValue);
822 v3_cpp_obj(fConnection)->notify(fConnection, message);
823
824 v3_cpp_obj_unref(message);
825 }
826
827 static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value)
828 {
829 static_cast<UIVst3*>(ptr)->setParameterValue(rindex, value);
830 }
831
832 #if DISTRHO_PLUGIN_WANT_STATE
833 void setState(const char* const key, const char* const value)
834 {
835 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
836
837 v3_message** const message = createMessage("state-set");
838 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
839
840 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
841 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
842
843 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
844 v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key));
845 v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value));
846 v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key));
847 v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value));
848 v3_cpp_obj(fConnection)->notify(fConnection, message);
849
850 v3_cpp_obj_unref(message);
851 }
852
853 static void setStateCallback(void* const ptr, const char* const key, const char* const value)
854 {
855 static_cast<UIVst3*>(ptr)->setState(key, value);
856 }
857 #endif
858
859 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
860 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
861 {
862 DISTRHO_SAFE_ASSERT_RETURN(fConnection != nullptr,);
863
864 v3_message** const message = createMessage("midi");
865 DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
866
867 v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
868 DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
869
870 uint8_t midiData[3];
871 midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel;
872 midiData[1] = note;
873 midiData[2] = velocity;
874
875 v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 1);
876 v3_cpp_obj(attrlist)->set_binary(attrlist, "data", midiData, sizeof(midiData));
877 v3_cpp_obj(fConnection)->notify(fConnection, message);
878
879 v3_cpp_obj_unref(message);
880 }
881
882 static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity)
883 {
884 static_cast<UIVst3*>(ptr)->sendNote(channel, note, velocity);
885 }
886 #endif
887
888 void setSize(uint width, uint height)
889 {
890 DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,);
891 DISTRHO_SAFE_ASSERT_RETURN(fFrame != nullptr,);
892
893 #ifdef DISTRHO_OS_MAC
894 const double scaleFactor = fUI.getScaleFactor();
895 width /= scaleFactor;
896 height /= scaleFactor;
897 #endif
898
899 if (fIsResizingFromHost)
900 {
901 if (fNeedsResizeFromPlugin)
902 {
903 d_debug("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height);
904 }
905 else
906 {
907 d_debug("plugin->host setSize %u %u (IGNORED, host resize active)", width, height);
908 return;
909 }
910 }
911 else
912 {
913 d_debug("plugin->host setSize %u %u (OK)", width, height);
914 }
915
916 fIsResizingFromPlugin = true;
917
918 v3_view_rect rect;
919 rect.left = rect.top = 0;
920 rect.right = width;
921 rect.bottom = height;
922 fNextPluginRect = rect;
923 v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect);
924 }
925
926 static void setSizeCallback(void* const ptr, const uint width, const uint height)
927 {
928 static_cast<UIVst3*>(ptr)->setSize(width, height);
929 }
930 };
931
932 // --------------------------------------------------------------------------------------------------------------------
933
934 /**
935 * VST3 low-level pointer thingies follow, proceed with care.
936 */
937
938 // --------------------------------------------------------------------------------------------------------------------
939 // v3_funknown for classes with a single instance
940
941 template<class T>
942 static uint32_t V3_API dpf_single_instance_ref(void* const self)
943 {
944 return ++(*static_cast<T**>(self))->refcounter;
945 }
946
947 template<class T>
948 static uint32_t V3_API dpf_single_instance_unref(void* const self)
949 {
950 return --(*static_cast<T**>(self))->refcounter;
951 }
952
953 // --------------------------------------------------------------------------------------------------------------------
954 // dpf_ui_connection_point
955
956 struct dpf_ui_connection_point : v3_connection_point_cpp {
957 std::atomic_int refcounter;
958 ScopedPointer<UIVst3>& uivst3;
959 v3_connection_point** other;
960
961 dpf_ui_connection_point(ScopedPointer<UIVst3>& v)
962 : refcounter(1),
963 uivst3(v),
964 other(nullptr)
965 {
966 // v3_funknown, single instance
967 query_interface = query_interface_connection_point;
968 ref = dpf_single_instance_ref<dpf_ui_connection_point>;
969 unref = dpf_single_instance_unref<dpf_ui_connection_point>;
970
971 // v3_connection_point
972 point.connect = connect;
973 point.disconnect = disconnect;
974 point.notify = notify;
975 }
976
977 // ----------------------------------------------------------------------------------------------------------------
978 // v3_funknown
979
980 static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface)
981 {
982 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self);
983
984 if (v3_tuid_match(iid, v3_funknown_iid) ||
985 v3_tuid_match(iid, v3_connection_point_iid))
986 {
987 d_debug("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface);
988 ++point->refcounter;
989 *iface = self;
990 return V3_OK;
991 }
992
993 d_debug("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
994
995 *iface = NULL;
996 return V3_NO_INTERFACE;
997 }
998
999 // ----------------------------------------------------------------------------------------------------------------
1000 // v3_connection_point
1001
1002 static v3_result V3_API connect(void* const self, v3_connection_point** const other)
1003 {
1004 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self);
1005 d_debug("UI|dpf_ui_connection_point::connect => %p %p", self, other);
1006
1007 DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
1008
1009 point->other = other;
1010
1011 if (UIVst3* const uivst3 = point->uivst3)
1012 uivst3->connect(other);
1013
1014 return V3_OK;
1015 };
1016
1017 static v3_result V3_API disconnect(void* const self, v3_connection_point** const other)
1018 {
1019 d_debug("UI|dpf_ui_connection_point::disconnect => %p %p", self, other);
1020 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self);
1021
1022 DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
1023 DISTRHO_SAFE_ASSERT(point->other == other);
1024
1025 point->other = nullptr;
1026
1027 if (UIVst3* const uivst3 = point->uivst3)
1028 uivst3->disconnect();
1029
1030 return V3_OK;
1031 };
1032
1033 static v3_result V3_API notify(void* const self, v3_message** const message)
1034 {
1035 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self);
1036
1037 UIVst3* const uivst3 = point->uivst3;
1038 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
1039
1040 return uivst3->notify(message);
1041 }
1042 };
1043
1044 // --------------------------------------------------------------------------------------------------------------------
1045 // dpf_plugin_view_content_scale
1046
1047 struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp {
1048 std::atomic_int refcounter;
1049 ScopedPointer<UIVst3>& uivst3;
1050 // cached values
1051 float scaleFactor;
1052
1053 dpf_plugin_view_content_scale(ScopedPointer<UIVst3>& v)
1054 : refcounter(1),
1055 uivst3(v),
1056 scaleFactor(0.0f)
1057 {
1058 // v3_funknown, single instance
1059 query_interface = query_interface_view_content_scale;
1060 ref = dpf_single_instance_ref<dpf_plugin_view_content_scale>;
1061 unref = dpf_single_instance_unref<dpf_plugin_view_content_scale>;
1062
1063 // v3_plugin_view_content_scale
1064 scale.set_content_scale_factor = set_content_scale_factor;
1065 }
1066
1067 // ----------------------------------------------------------------------------------------------------------------
1068 // v3_funknown
1069
1070 static v3_result V3_API query_interface_view_content_scale(void* const self, const v3_tuid iid, void** const iface)
1071 {
1072 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self);
1073
1074 if (v3_tuid_match(iid, v3_funknown_iid) ||
1075 v3_tuid_match(iid, v3_plugin_view_content_scale_iid))
1076 {
1077 d_debug("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface);
1078 ++scale->refcounter;
1079 *iface = self;
1080 return V3_OK;
1081 }
1082
1083 d_debug("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
1084
1085 *iface = NULL;
1086 return V3_NO_INTERFACE;
1087 }
1088
1089 // ----------------------------------------------------------------------------------------------------------------
1090 // v3_plugin_view_content_scale
1091
1092 static v3_result V3_API set_content_scale_factor(void* const self, const float factor)
1093 {
1094 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self);
1095 d_debug("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor);
1096
1097 scale->scaleFactor = factor;
1098
1099 if (UIVst3* const uivst3 = scale->uivst3)
1100 return uivst3->setContentScaleFactor(factor);
1101
1102 return V3_NOT_INITIALIZED;
1103 }
1104 };
1105
1106 #if DPF_VST3_USING_HOST_RUN_LOOP
1107 // --------------------------------------------------------------------------------------------------------------------
1108 // dpf_timer_handler
1109
1110 struct dpf_timer_handler : v3_timer_handler_cpp {
1111 std::atomic_int refcounter;
1112 ScopedPointer<UIVst3>& uivst3;
1113 bool valid;
1114
1115 dpf_timer_handler(ScopedPointer<UIVst3>& v)
1116 : refcounter(1),
1117 uivst3(v),
1118 valid(true)
1119 {
1120 // v3_funknown, single instance
1121 query_interface = query_interface_timer_handler;
1122 ref = dpf_single_instance_ref<dpf_timer_handler>;
1123 unref = dpf_single_instance_unref<dpf_timer_handler>;
1124
1125 // v3_timer_handler
1126 timer.on_timer = on_timer;
1127 }
1128
1129 // ----------------------------------------------------------------------------------------------------------------
1130 // v3_funknown
1131
1132 static v3_result V3_API query_interface_timer_handler(void* self, const v3_tuid iid, void** iface)
1133 {
1134 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self);
1135
1136 if (v3_tuid_match(iid, v3_funknown_iid) ||
1137 v3_tuid_match(iid, v3_timer_handler_iid))
1138 {
1139 d_debug("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface);
1140 ++timer->refcounter;
1141 *iface = self;
1142 return V3_OK;
1143 }
1144
1145 d_debug("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
1146
1147 *iface = NULL;
1148 return V3_NO_INTERFACE;
1149 }
1150
1151 // ----------------------------------------------------------------------------------------------------------------
1152 // v3_timer_handler
1153
1154 static void V3_API on_timer(void* self)
1155 {
1156 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self);
1157
1158 DISTRHO_SAFE_ASSERT_RETURN(timer->valid,);
1159
1160 timer->uivst3->onTimer();
1161 }
1162 };
1163 #endif
1164
1165 // --------------------------------------------------------------------------------------------------------------------
1166 // dpf_plugin_view
1167
1168 static const char* const kSupportedPlatforms[] = {
1169 #if defined(DISTRHO_OS_WINDOWS)
1170 V3_VIEW_PLATFORM_TYPE_HWND,
1171 #elif defined(DISTRHO_OS_MAC)
1172 V3_VIEW_PLATFORM_TYPE_NSVIEW,
1173 #else
1174 V3_VIEW_PLATFORM_TYPE_X11,
1175 #endif
1176 };
1177
1178 struct dpf_plugin_view : v3_plugin_view_cpp {
1179 std::atomic_int refcounter;
1180 ScopedPointer<dpf_ui_connection_point> connection;
1181 ScopedPointer<dpf_plugin_view_content_scale> scale;
1182 #if DPF_VST3_USING_HOST_RUN_LOOP
1183 ScopedPointer<dpf_timer_handler> timer;
1184 #endif
1185 ScopedPointer<UIVst3> uivst3;
1186 // cached values
1187 v3_host_application** const hostApplication;
1188 void* const instancePointer;
1189 double sampleRate;
1190 v3_plugin_frame** frame;
1191 v3_run_loop** runloop;
1192 uint32_t nextWidth, nextHeight;
1193 bool sizeRequestedBeforeBeingAttached;
1194
1195 dpf_plugin_view(v3_host_application** const host, void* const instance, const double sr)
1196 : refcounter(1),
1197 hostApplication(host),
1198 instancePointer(instance),
1199 sampleRate(sr),
1200 frame(nullptr),
1201 runloop(nullptr),
1202 nextWidth(0),
1203 nextHeight(0),
1204 sizeRequestedBeforeBeingAttached(false)
1205 {
1206 d_debug("dpf_plugin_view() with hostApplication %p", hostApplication);
1207
1208 // make sure host application is valid through out this view lifetime
1209 if (hostApplication != nullptr)
1210 v3_cpp_obj_ref(hostApplication);
1211
1212 // v3_funknown, everything custom
1213 query_interface = query_interface_view;
1214 ref = ref_view;
1215 unref = unref_view;
1216
1217 // v3_plugin_view
1218 view.is_platform_type_supported = is_platform_type_supported;
1219 view.attached = attached;
1220 view.removed = removed;
1221 view.on_wheel = on_wheel;
1222 view.on_key_down = on_key_down;
1223 view.on_key_up = on_key_up;
1224 view.get_size = get_size;
1225 view.on_size = on_size;
1226 view.on_focus = on_focus;
1227 view.set_frame = set_frame;
1228 view.can_resize = can_resize;
1229 view.check_size_constraint = check_size_constraint;
1230 }
1231
1232 ~dpf_plugin_view()
1233 {
1234 d_debug("~dpf_plugin_view()");
1235
1236 connection = nullptr;
1237 scale = nullptr;
1238 #if DPF_VST3_USING_HOST_RUN_LOOP
1239 timer = nullptr;
1240 #endif
1241 uivst3 = nullptr;
1242
1243 if (hostApplication != nullptr)
1244 v3_cpp_obj_unref(hostApplication);
1245 }
1246
1247 // ----------------------------------------------------------------------------------------------------------------
1248 // v3_funknown
1249
1250 static v3_result V3_API query_interface_view(void* self, const v3_tuid iid, void** iface)
1251 {
1252 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1253
1254 if (v3_tuid_match(iid, v3_funknown_iid) ||
1255 v3_tuid_match(iid, v3_plugin_view_iid))
1256 {
1257 d_debug("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface);
1258 ++view->refcounter;
1259 *iface = self;
1260 return V3_OK;
1261 }
1262
1263 if (v3_tuid_match(v3_connection_point_iid, iid))
1264 {
1265 d_debug("query_interface_view => %p %s %p | OK convert %p",
1266 self, tuid2str(iid), iface, view->connection.get());
1267
1268 if (view->connection == nullptr)
1269 view->connection = new dpf_ui_connection_point(view->uivst3);
1270 else
1271 ++view->connection->refcounter;
1272 *iface = &view->connection;
1273 return V3_OK;
1274 }
1275
1276 #ifndef DISTRHO_OS_MAC
1277 if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid))
1278 {
1279 d_debug("query_interface_view => %p %s %p | OK convert %p",
1280 self, tuid2str(iid), iface, view->scale.get());
1281
1282 if (view->scale == nullptr)
1283 view->scale = new dpf_plugin_view_content_scale(view->uivst3);
1284 else
1285 ++view->scale->refcounter;
1286 *iface = &view->scale;
1287 return V3_OK;
1288 }
1289 #endif
1290
1291 d_debug("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
1292
1293 *iface = nullptr;
1294 return V3_NO_INTERFACE;
1295 }
1296
1297 static uint32_t V3_API ref_view(void* self)
1298 {
1299 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1300 const int refcount = ++view->refcounter;
1301 d_debug("dpf_plugin_view::ref => %p | refcount %i", self, refcount);
1302 return refcount;
1303 }
1304
1305 static uint32_t V3_API unref_view(void* self)
1306 {
1307 dpf_plugin_view** const viewptr = static_cast<dpf_plugin_view**>(self);
1308 dpf_plugin_view* const view = *viewptr;
1309
1310 if (const int refcount = --view->refcounter)
1311 {
1312 d_debug("dpf_plugin_view::unref => %p | refcount %i", self, refcount);
1313 return refcount;
1314 }
1315
1316 if (view->connection != nullptr && view->connection->other)
1317 v3_cpp_obj(view->connection->other)->disconnect(view->connection->other,
1318 (v3_connection_point**)&view->connection);
1319
1320 /**
1321 * Some hosts will have unclean instances of a few of the view child classes at this point.
1322 * We check for those here, going through the whole possible chain to see if it is safe to delete.
1323 * TODO cleanup.
1324 */
1325
1326 bool unclean = false;
1327
1328 if (dpf_ui_connection_point* const conn = view->connection)
1329 {
1330 if (const int refcount = conn->refcounter)
1331 {
1332 unclean = true;
1333 d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount);
1334 }
1335 }
1336
1337 #ifndef DISTRHO_OS_MAC
1338 if (dpf_plugin_view_content_scale* const scale = view->scale)
1339 {
1340 if (const int refcount = scale->refcounter)
1341 {
1342 unclean = true;
1343 d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount);
1344 }
1345 }
1346 #endif
1347
1348 if (unclean)
1349 return 0;
1350
1351 d_debug("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self);
1352
1353 delete view;
1354 delete viewptr;
1355 return 0;
1356 }
1357
1358 // ----------------------------------------------------------------------------------------------------------------
1359 // v3_plugin_view
1360
1361 static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type)
1362 {
1363 d_debug("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type);
1364
1365 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i)
1366 {
1367 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0)
1368 return V3_OK;
1369 }
1370
1371 return V3_NOT_IMPLEMENTED;
1372
1373 // unused unless debug
1374 (void)self;
1375 }
1376
1377 static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type)
1378 {
1379 d_debug("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type);
1380 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1381 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG);
1382
1383 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i)
1384 {
1385 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0)
1386 {
1387 #if DPF_VST3_USING_HOST_RUN_LOOP
1388 // find host run loop to plug ourselves into (required on some systems)
1389 DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG);
1390
1391 v3_run_loop** runloop = nullptr;
1392 v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop);
1393 DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG);
1394
1395 view->runloop = runloop;
1396 #endif
1397
1398 const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f;
1399 view->uivst3 = new UIVst3((v3_plugin_view**)self,
1400 view->hostApplication,
1401 view->connection != nullptr ? view->connection->other : nullptr,
1402 view->frame,
1403 (uintptr_t)parent,
1404 lastScaleFactor,
1405 view->sampleRate,
1406 view->instancePointer,
1407 view->nextWidth > 0 && view->nextHeight > 0,
1408 view->sizeRequestedBeforeBeingAttached);
1409
1410 view->uivst3->postInit(view->nextWidth, view->nextHeight);
1411 view->nextWidth = 0;
1412 view->nextHeight = 0;
1413 view->sizeRequestedBeforeBeingAttached = false;
1414
1415 #if DPF_VST3_USING_HOST_RUN_LOOP
1416 // register a timer host run loop stuff
1417 view->timer = new dpf_timer_handler(view->uivst3);
1418 v3_cpp_obj(runloop)->register_timer(runloop,
1419 (v3_timer_handler**)&view->timer,
1420 DPF_VST3_TIMER_INTERVAL);
1421 #endif
1422
1423 return V3_OK;
1424 }
1425 }
1426
1427 return V3_NOT_IMPLEMENTED;
1428 }
1429
1430 static v3_result V3_API removed(void* const self)
1431 {
1432 d_debug("dpf_plugin_view::removed => %p", self);
1433 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1434 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG);
1435
1436 #if DPF_VST3_USING_HOST_RUN_LOOP
1437 // unregister our timer as needed
1438 if (v3_run_loop** const runloop = view->runloop)
1439 {
1440 if (view->timer != nullptr && view->timer->valid)
1441 {
1442 v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer);
1443
1444 if (const int refcount = --view->timer->refcounter)
1445 {
1446 view->timer->valid = false;
1447 d_stderr("VST3 warning: Host run loop did not give away timer (refcount %d)", refcount);
1448 }
1449 else
1450 {
1451 view->timer = nullptr;
1452 }
1453 }
1454
1455 v3_cpp_obj_unref(runloop);
1456 view->runloop = nullptr;
1457 }
1458 #endif
1459
1460 view->uivst3 = nullptr;
1461 return V3_OK;
1462 }
1463
1464 static v3_result V3_API on_wheel(void* const self, const float distance)
1465 {
1466 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
1467 d_debug("dpf_plugin_view::on_wheel => %p %f", self, distance);
1468 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1469
1470 UIVst3* const uivst3 = view->uivst3;
1471 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
1472
1473 return uivst3->onWheel(distance);
1474 #else
1475 return V3_NOT_IMPLEMENTED;
1476 // unused
1477 (void)self; (void)distance;
1478 #endif
1479 }
1480
1481 static v3_result V3_API on_key_down(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers)
1482 {
1483 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
1484 d_debug("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers);
1485 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1486
1487 UIVst3* const uivst3 = view->uivst3;
1488 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
1489
1490 return uivst3->onKeyDown(key_char, key_code, modifiers);
1491 #else
1492 return V3_NOT_IMPLEMENTED;
1493 // unused
1494 (void)self; (void)key_char; (void)key_code; (void)modifiers;
1495 #endif
1496 }
1497
1498 static v3_result V3_API on_key_up(void* const self, const int16_t key_char, const int16_t key_code, const int16_t modifiers)
1499 {
1500 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
1501 d_debug("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers);
1502 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1503
1504 UIVst3* const uivst3 = view->uivst3;
1505 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
1506
1507 return uivst3->onKeyUp(key_char, key_code, modifiers);
1508 #else
1509 return V3_NOT_IMPLEMENTED;
1510 // unused
1511 (void)self; (void)key_char; (void)key_code; (void)modifiers;
1512 #endif
1513 }
1514
1515 static v3_result V3_API get_size(void* const self, v3_view_rect* const rect)
1516 {
1517 d_debug("dpf_plugin_view::get_size => %p", self);
1518 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1519
1520 if (UIVst3* const uivst3 = view->uivst3)
1521 return uivst3->getSize(rect);
1522
1523 d_debug("dpf_plugin_view::get_size => %p | NOTE: size request before attach", self);
1524
1525 view->sizeRequestedBeforeBeingAttached = true;
1526
1527 double scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0;
1528 #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT)
1529 rect->right = DISTRHO_UI_DEFAULT_WIDTH;
1530 rect->bottom = DISTRHO_UI_DEFAULT_HEIGHT;
1531 if (d_isZero(scaleFactor))
1532 scaleFactor = 1.0;
1533 #else
1534 UIExporter tmpUI(nullptr, 0, view->sampleRate,
1535 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath,
1536 view->instancePointer, scaleFactor);
1537 rect->right = tmpUI.getWidth();
1538 rect->bottom = tmpUI.getHeight();
1539 scaleFactor = tmpUI.getScaleFactor();
1540 tmpUI.quit();
1541 #endif
1542 rect->left = rect->top = 0;
1543 #ifdef DISTRHO_OS_MAC
1544 rect->right /= scaleFactor;
1545 rect->bottom /= scaleFactor;
1546 #endif
1547
1548 return V3_OK;
1549 }
1550
1551 static v3_result V3_API on_size(void* const self, v3_view_rect* const rect)
1552 {
1553 d_debug("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}",
1554 self, rect->top, rect->left, rect->right, rect->bottom);
1555 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->right > rect->left, rect->right, rect->left, V3_INVALID_ARG);
1556 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->bottom > rect->top, rect->bottom, rect->top, V3_INVALID_ARG);
1557
1558 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1559
1560 if (UIVst3* const uivst3 = view->uivst3)
1561 return uivst3->onSize(rect);
1562
1563 view->nextWidth = static_cast<uint32_t>(rect->right - rect->left);
1564 view->nextHeight = static_cast<uint32_t>(rect->bottom - rect->top);
1565 return V3_OK;
1566 }
1567
1568 static v3_result V3_API on_focus(void* const self, const v3_bool state)
1569 {
1570 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
1571 d_debug("dpf_plugin_view::on_focus => %p %u", self, state);
1572 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1573
1574 UIVst3* const uivst3 = view->uivst3;
1575 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED);
1576
1577 return uivst3->onFocus(state);
1578 #else
1579 return V3_NOT_IMPLEMENTED;
1580 // unused
1581 (void)self; (void)state;
1582 #endif
1583 }
1584
1585 static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame)
1586 {
1587 d_debug("dpf_plugin_view::set_frame => %p %p", self, frame);
1588 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1589
1590 view->frame = frame;
1591
1592 if (UIVst3* const uivst3 = view->uivst3)
1593 return uivst3->setFrame(frame);
1594
1595 return V3_OK;
1596 }
1597
1598 static v3_result V3_API can_resize(void* const self)
1599 {
1600 #if DISTRHO_UI_USER_RESIZABLE
1601 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1602
1603 if (UIVst3* const uivst3 = view->uivst3)
1604 return uivst3->canResize();
1605
1606 return V3_TRUE;
1607 #else
1608 return V3_FALSE;
1609
1610 // unused
1611 (void)self;
1612 #endif
1613 }
1614
1615 static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect)
1616 {
1617 d_debug("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}",
1618 self, rect->top, rect->left, rect->right, rect->bottom);
1619 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self);
1620
1621 if (UIVst3* const uivst3 = view->uivst3)
1622 return uivst3->checkSizeConstraint(rect);
1623
1624 return V3_NOT_INITIALIZED;
1625 }
1626 };
1627
1628 // --------------------------------------------------------------------------------------------------------------------
1629 // dpf_plugin_view_create (called from plugin side)
1630
1631 v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);
1632
1633 v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host,
1634 void* const instancePointer,
1635 const double sampleRate)
1636 {
1637 dpf_plugin_view** const viewptr = new dpf_plugin_view*;
1638 *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate);
1639 return static_cast<v3_plugin_view**>(static_cast<void*>(viewptr));
1640 }
1641
1642 // --------------------------------------------------------------------------------------------------------------------
1643
1644 END_NAMESPACE_DISTRHO