DistrhoUIVST3.cpp (56976B)
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 = d_roundToIntPositive(rect->bottom * ratio); 96 // fix height 97 else 98 rect->bottom = d_roundToIntPositive(static_cast<double>(rect->right) / ratio); 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 DISTRHO_SAFE_ASSERT_RETURN(width != 0 && height != 0,); 893 894 #ifdef DISTRHO_OS_MAC 895 const double scaleFactor = fUI.getScaleFactor(); 896 width /= scaleFactor; 897 height /= scaleFactor; 898 #endif 899 900 if (fIsResizingFromHost) 901 { 902 if (fNeedsResizeFromPlugin) 903 { 904 d_debug("plugin->host setSize %u %u (FORCED, exception for first resize)", width, height); 905 } 906 else 907 { 908 d_debug("plugin->host setSize %u %u (IGNORED, host resize active)", width, height); 909 return; 910 } 911 } 912 else 913 { 914 d_debug("plugin->host setSize %u %u (OK)", width, height); 915 } 916 917 fIsResizingFromPlugin = true; 918 919 v3_view_rect rect; 920 rect.left = rect.top = 0; 921 rect.right = width; 922 rect.bottom = height; 923 fNextPluginRect = rect; 924 v3_cpp_obj(fFrame)->resize_view(fFrame, fView, &rect); 925 } 926 927 static void setSizeCallback(void* const ptr, const uint width, const uint height) 928 { 929 static_cast<UIVst3*>(ptr)->setSize(width, height); 930 } 931 }; 932 933 // -------------------------------------------------------------------------------------------------------------------- 934 935 /** 936 * VST3 low-level pointer thingies follow, proceed with care. 937 */ 938 939 // -------------------------------------------------------------------------------------------------------------------- 940 // v3_funknown for classes with a single instance 941 942 template<class T> 943 static uint32_t V3_API dpf_single_instance_ref(void* const self) 944 { 945 return ++(*static_cast<T**>(self))->refcounter; 946 } 947 948 template<class T> 949 static uint32_t V3_API dpf_single_instance_unref(void* const self) 950 { 951 return --(*static_cast<T**>(self))->refcounter; 952 } 953 954 // -------------------------------------------------------------------------------------------------------------------- 955 // dpf_ui_connection_point 956 957 struct dpf_ui_connection_point : v3_connection_point_cpp { 958 std::atomic_int refcounter; 959 ScopedPointer<UIVst3>& uivst3; 960 v3_connection_point** other; 961 962 dpf_ui_connection_point(ScopedPointer<UIVst3>& v) 963 : refcounter(1), 964 uivst3(v), 965 other(nullptr) 966 { 967 // v3_funknown, single instance 968 query_interface = query_interface_connection_point; 969 ref = dpf_single_instance_ref<dpf_ui_connection_point>; 970 unref = dpf_single_instance_unref<dpf_ui_connection_point>; 971 972 // v3_connection_point 973 point.connect = connect; 974 point.disconnect = disconnect; 975 point.notify = notify; 976 } 977 978 // ---------------------------------------------------------------------------------------------------------------- 979 // v3_funknown 980 981 static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface) 982 { 983 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); 984 985 if (v3_tuid_match(iid, v3_funknown_iid) || 986 v3_tuid_match(iid, v3_connection_point_iid)) 987 { 988 d_debug("UI|query_interface_connection_point => %p %s %p | OK", self, tuid2str(iid), iface); 989 ++point->refcounter; 990 *iface = self; 991 return V3_OK; 992 } 993 994 d_debug("DSP|query_interface_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); 995 996 *iface = NULL; 997 return V3_NO_INTERFACE; 998 } 999 1000 // ---------------------------------------------------------------------------------------------------------------- 1001 // v3_connection_point 1002 1003 static v3_result V3_API connect(void* const self, v3_connection_point** const other) 1004 { 1005 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); 1006 d_debug("UI|dpf_ui_connection_point::connect => %p %p", self, other); 1007 1008 DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG); 1009 1010 point->other = other; 1011 1012 if (UIVst3* const uivst3 = point->uivst3) 1013 uivst3->connect(other); 1014 1015 return V3_OK; 1016 }; 1017 1018 static v3_result V3_API disconnect(void* const self, v3_connection_point** const other) 1019 { 1020 d_debug("UI|dpf_ui_connection_point::disconnect => %p %p", self, other); 1021 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); 1022 1023 DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG); 1024 DISTRHO_SAFE_ASSERT(point->other == other); 1025 1026 point->other = nullptr; 1027 1028 if (UIVst3* const uivst3 = point->uivst3) 1029 uivst3->disconnect(); 1030 1031 return V3_OK; 1032 }; 1033 1034 static v3_result V3_API notify(void* const self, v3_message** const message) 1035 { 1036 dpf_ui_connection_point* const point = *static_cast<dpf_ui_connection_point**>(self); 1037 1038 UIVst3* const uivst3 = point->uivst3; 1039 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); 1040 1041 return uivst3->notify(message); 1042 } 1043 }; 1044 1045 // -------------------------------------------------------------------------------------------------------------------- 1046 // dpf_plugin_view_content_scale 1047 1048 struct dpf_plugin_view_content_scale : v3_plugin_view_content_scale_cpp { 1049 std::atomic_int refcounter; 1050 ScopedPointer<UIVst3>& uivst3; 1051 // cached values 1052 float scaleFactor; 1053 1054 dpf_plugin_view_content_scale(ScopedPointer<UIVst3>& v) 1055 : refcounter(1), 1056 uivst3(v), 1057 scaleFactor(0.0f) 1058 { 1059 // v3_funknown, single instance 1060 query_interface = query_interface_view_content_scale; 1061 ref = dpf_single_instance_ref<dpf_plugin_view_content_scale>; 1062 unref = dpf_single_instance_unref<dpf_plugin_view_content_scale>; 1063 1064 // v3_plugin_view_content_scale 1065 scale.set_content_scale_factor = set_content_scale_factor; 1066 } 1067 1068 // ---------------------------------------------------------------------------------------------------------------- 1069 // v3_funknown 1070 1071 static v3_result V3_API query_interface_view_content_scale(void* const self, const v3_tuid iid, void** const iface) 1072 { 1073 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self); 1074 1075 if (v3_tuid_match(iid, v3_funknown_iid) || 1076 v3_tuid_match(iid, v3_plugin_view_content_scale_iid)) 1077 { 1078 d_debug("query_interface_view_content_scale => %p %s %p | OK", self, tuid2str(iid), iface); 1079 ++scale->refcounter; 1080 *iface = self; 1081 return V3_OK; 1082 } 1083 1084 d_debug("query_interface_view_content_scale => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); 1085 1086 *iface = NULL; 1087 return V3_NO_INTERFACE; 1088 } 1089 1090 // ---------------------------------------------------------------------------------------------------------------- 1091 // v3_plugin_view_content_scale 1092 1093 static v3_result V3_API set_content_scale_factor(void* const self, const float factor) 1094 { 1095 dpf_plugin_view_content_scale* const scale = *static_cast<dpf_plugin_view_content_scale**>(self); 1096 d_debug("dpf_plugin_view::set_content_scale_factor => %p %f", self, factor); 1097 1098 scale->scaleFactor = factor; 1099 1100 if (UIVst3* const uivst3 = scale->uivst3) 1101 return uivst3->setContentScaleFactor(factor); 1102 1103 return V3_NOT_INITIALIZED; 1104 } 1105 }; 1106 1107 #if DPF_VST3_USING_HOST_RUN_LOOP 1108 // -------------------------------------------------------------------------------------------------------------------- 1109 // dpf_timer_handler 1110 1111 struct dpf_timer_handler : v3_timer_handler_cpp { 1112 std::atomic_int refcounter; 1113 ScopedPointer<UIVst3>& uivst3; 1114 bool valid; 1115 1116 dpf_timer_handler(ScopedPointer<UIVst3>& v) 1117 : refcounter(1), 1118 uivst3(v), 1119 valid(true) 1120 { 1121 // v3_funknown, single instance 1122 query_interface = query_interface_timer_handler; 1123 ref = dpf_single_instance_ref<dpf_timer_handler>; 1124 unref = dpf_single_instance_unref<dpf_timer_handler>; 1125 1126 // v3_timer_handler 1127 timer.on_timer = on_timer; 1128 } 1129 1130 // ---------------------------------------------------------------------------------------------------------------- 1131 // v3_funknown 1132 1133 static v3_result V3_API query_interface_timer_handler(void* self, const v3_tuid iid, void** iface) 1134 { 1135 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self); 1136 1137 if (v3_tuid_match(iid, v3_funknown_iid) || 1138 v3_tuid_match(iid, v3_timer_handler_iid)) 1139 { 1140 d_debug("query_interface_timer_handler => %p %s %p | OK", self, tuid2str(iid), iface); 1141 ++timer->refcounter; 1142 *iface = self; 1143 return V3_OK; 1144 } 1145 1146 d_debug("query_interface_timer_handler => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); 1147 1148 *iface = NULL; 1149 return V3_NO_INTERFACE; 1150 } 1151 1152 // ---------------------------------------------------------------------------------------------------------------- 1153 // v3_timer_handler 1154 1155 static void V3_API on_timer(void* self) 1156 { 1157 dpf_timer_handler* const timer = *static_cast<dpf_timer_handler**>(self); 1158 1159 DISTRHO_SAFE_ASSERT_RETURN(timer->valid,); 1160 1161 timer->uivst3->onTimer(); 1162 } 1163 }; 1164 #endif 1165 1166 // -------------------------------------------------------------------------------------------------------------------- 1167 // dpf_plugin_view 1168 1169 static const char* const kSupportedPlatforms[] = { 1170 #if defined(DISTRHO_OS_WINDOWS) 1171 V3_VIEW_PLATFORM_TYPE_HWND, 1172 #elif defined(DISTRHO_OS_MAC) 1173 V3_VIEW_PLATFORM_TYPE_NSVIEW, 1174 #else 1175 V3_VIEW_PLATFORM_TYPE_X11, 1176 #endif 1177 }; 1178 1179 struct dpf_plugin_view : v3_plugin_view_cpp { 1180 std::atomic_int refcounter; 1181 ScopedPointer<dpf_ui_connection_point> connection; 1182 ScopedPointer<dpf_plugin_view_content_scale> scale; 1183 #if DPF_VST3_USING_HOST_RUN_LOOP 1184 ScopedPointer<dpf_timer_handler> timer; 1185 #endif 1186 ScopedPointer<UIVst3> uivst3; 1187 // cached values 1188 v3_host_application** const hostApplication; 1189 void* const instancePointer; 1190 double sampleRate; 1191 v3_plugin_frame** frame; 1192 v3_run_loop** runloop; 1193 uint32_t nextWidth, nextHeight; 1194 bool sizeRequestedBeforeBeingAttached; 1195 1196 dpf_plugin_view(v3_host_application** const host, void* const instance, const double sr) 1197 : refcounter(1), 1198 hostApplication(host), 1199 instancePointer(instance), 1200 sampleRate(sr), 1201 frame(nullptr), 1202 runloop(nullptr), 1203 nextWidth(0), 1204 nextHeight(0), 1205 sizeRequestedBeforeBeingAttached(false) 1206 { 1207 d_debug("dpf_plugin_view() with hostApplication %p", hostApplication); 1208 1209 // make sure host application is valid through out this view lifetime 1210 if (hostApplication != nullptr) 1211 v3_cpp_obj_ref(hostApplication); 1212 1213 // v3_funknown, everything custom 1214 query_interface = query_interface_view; 1215 ref = ref_view; 1216 unref = unref_view; 1217 1218 // v3_plugin_view 1219 view.is_platform_type_supported = is_platform_type_supported; 1220 view.attached = attached; 1221 view.removed = removed; 1222 view.on_wheel = on_wheel; 1223 view.on_key_down = on_key_down; 1224 view.on_key_up = on_key_up; 1225 view.get_size = get_size; 1226 view.on_size = on_size; 1227 view.on_focus = on_focus; 1228 view.set_frame = set_frame; 1229 view.can_resize = can_resize; 1230 view.check_size_constraint = check_size_constraint; 1231 } 1232 1233 ~dpf_plugin_view() 1234 { 1235 d_debug("~dpf_plugin_view()"); 1236 1237 connection = nullptr; 1238 scale = nullptr; 1239 #if DPF_VST3_USING_HOST_RUN_LOOP 1240 timer = nullptr; 1241 #endif 1242 uivst3 = nullptr; 1243 1244 if (hostApplication != nullptr) 1245 v3_cpp_obj_unref(hostApplication); 1246 } 1247 1248 // ---------------------------------------------------------------------------------------------------------------- 1249 // v3_funknown 1250 1251 static v3_result V3_API query_interface_view(void* self, const v3_tuid iid, void** iface) 1252 { 1253 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1254 1255 if (v3_tuid_match(iid, v3_funknown_iid) || 1256 v3_tuid_match(iid, v3_plugin_view_iid)) 1257 { 1258 d_debug("query_interface_view => %p %s %p | OK", self, tuid2str(iid), iface); 1259 ++view->refcounter; 1260 *iface = self; 1261 return V3_OK; 1262 } 1263 1264 if (v3_tuid_match(v3_connection_point_iid, iid)) 1265 { 1266 d_debug("query_interface_view => %p %s %p | OK convert %p", 1267 self, tuid2str(iid), iface, view->connection.get()); 1268 1269 if (view->connection == nullptr) 1270 view->connection = new dpf_ui_connection_point(view->uivst3); 1271 else 1272 ++view->connection->refcounter; 1273 *iface = &view->connection; 1274 return V3_OK; 1275 } 1276 1277 #ifndef DISTRHO_OS_MAC 1278 if (v3_tuid_match(v3_plugin_view_content_scale_iid, iid)) 1279 { 1280 d_debug("query_interface_view => %p %s %p | OK convert %p", 1281 self, tuid2str(iid), iface, view->scale.get()); 1282 1283 if (view->scale == nullptr) 1284 view->scale = new dpf_plugin_view_content_scale(view->uivst3); 1285 else 1286 ++view->scale->refcounter; 1287 *iface = &view->scale; 1288 return V3_OK; 1289 } 1290 #endif 1291 1292 d_debug("query_interface_view => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface); 1293 1294 *iface = nullptr; 1295 return V3_NO_INTERFACE; 1296 } 1297 1298 static uint32_t V3_API ref_view(void* self) 1299 { 1300 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1301 const int refcount = ++view->refcounter; 1302 d_debug("dpf_plugin_view::ref => %p | refcount %i", self, refcount); 1303 return refcount; 1304 } 1305 1306 static uint32_t V3_API unref_view(void* self) 1307 { 1308 dpf_plugin_view** const viewptr = static_cast<dpf_plugin_view**>(self); 1309 dpf_plugin_view* const view = *viewptr; 1310 1311 if (const int refcount = --view->refcounter) 1312 { 1313 d_debug("dpf_plugin_view::unref => %p | refcount %i", self, refcount); 1314 return refcount; 1315 } 1316 1317 if (view->connection != nullptr && view->connection->other) 1318 v3_cpp_obj(view->connection->other)->disconnect(view->connection->other, 1319 (v3_connection_point**)&view->connection); 1320 1321 /** 1322 * Some hosts will have unclean instances of a few of the view child classes at this point. 1323 * We check for those here, going through the whole possible chain to see if it is safe to delete. 1324 * TODO cleanup. 1325 */ 1326 1327 bool unclean = false; 1328 1329 if (dpf_ui_connection_point* const conn = view->connection) 1330 { 1331 if (const int refcount = conn->refcounter) 1332 { 1333 unclean = true; 1334 d_stderr("DPF warning: asked to delete view while connection point still active (refcount %d)", refcount); 1335 } 1336 } 1337 1338 #ifndef DISTRHO_OS_MAC 1339 if (dpf_plugin_view_content_scale* const scale = view->scale) 1340 { 1341 if (const int refcount = scale->refcounter) 1342 { 1343 unclean = true; 1344 d_stderr("DPF warning: asked to delete view while content scale still active (refcount %d)", refcount); 1345 } 1346 } 1347 #endif 1348 1349 if (unclean) 1350 return 0; 1351 1352 d_debug("dpf_plugin_view::unref => %p | refcount is zero, deleting everything now!", self); 1353 1354 delete view; 1355 delete viewptr; 1356 return 0; 1357 } 1358 1359 // ---------------------------------------------------------------------------------------------------------------- 1360 // v3_plugin_view 1361 1362 static v3_result V3_API is_platform_type_supported(void* const self, const char* const platform_type) 1363 { 1364 d_debug("dpf_plugin_view::is_platform_type_supported => %p %s", self, platform_type); 1365 1366 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i) 1367 { 1368 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) 1369 return V3_OK; 1370 } 1371 1372 return V3_NOT_IMPLEMENTED; 1373 1374 // unused unless debug 1375 (void)self; 1376 } 1377 1378 static v3_result V3_API attached(void* const self, void* const parent, const char* const platform_type) 1379 { 1380 d_debug("dpf_plugin_view::attached => %p %p %s", self, parent, platform_type); 1381 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1382 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 == nullptr, V3_INVALID_ARG); 1383 1384 for (size_t i=0; i<ARRAY_SIZE(kSupportedPlatforms); ++i) 1385 { 1386 if (std::strcmp(kSupportedPlatforms[i], platform_type) == 0) 1387 { 1388 #if DPF_VST3_USING_HOST_RUN_LOOP 1389 // find host run loop to plug ourselves into (required on some systems) 1390 DISTRHO_SAFE_ASSERT_RETURN(view->frame != nullptr, V3_INVALID_ARG); 1391 1392 v3_run_loop** runloop = nullptr; 1393 v3_cpp_obj_query_interface(view->frame, v3_run_loop_iid, &runloop); 1394 DISTRHO_SAFE_ASSERT_RETURN(runloop != nullptr, V3_INVALID_ARG); 1395 1396 view->runloop = runloop; 1397 #endif 1398 1399 const float lastScaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0f; 1400 view->uivst3 = new UIVst3((v3_plugin_view**)self, 1401 view->hostApplication, 1402 view->connection != nullptr ? view->connection->other : nullptr, 1403 view->frame, 1404 (uintptr_t)parent, 1405 lastScaleFactor, 1406 view->sampleRate, 1407 view->instancePointer, 1408 view->nextWidth > 0 && view->nextHeight > 0, 1409 view->sizeRequestedBeforeBeingAttached); 1410 1411 view->uivst3->postInit(view->nextWidth, view->nextHeight); 1412 view->nextWidth = 0; 1413 view->nextHeight = 0; 1414 view->sizeRequestedBeforeBeingAttached = false; 1415 1416 #if DPF_VST3_USING_HOST_RUN_LOOP 1417 // register a timer host run loop stuff 1418 view->timer = new dpf_timer_handler(view->uivst3); 1419 v3_cpp_obj(runloop)->register_timer(runloop, 1420 (v3_timer_handler**)&view->timer, 1421 DPF_VST3_TIMER_INTERVAL); 1422 #endif 1423 1424 return V3_OK; 1425 } 1426 } 1427 1428 return V3_NOT_IMPLEMENTED; 1429 } 1430 1431 static v3_result V3_API removed(void* const self) 1432 { 1433 d_debug("dpf_plugin_view::removed => %p", self); 1434 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1435 DISTRHO_SAFE_ASSERT_RETURN(view->uivst3 != nullptr, V3_INVALID_ARG); 1436 1437 #if DPF_VST3_USING_HOST_RUN_LOOP 1438 // unregister our timer as needed 1439 if (v3_run_loop** const runloop = view->runloop) 1440 { 1441 if (view->timer != nullptr && view->timer->valid) 1442 { 1443 v3_cpp_obj(runloop)->unregister_timer(runloop, (v3_timer_handler**)&view->timer); 1444 1445 if (const int refcount = --view->timer->refcounter) 1446 { 1447 view->timer->valid = false; 1448 d_stderr("VST3 warning: Host run loop did not give away timer (refcount %d)", refcount); 1449 } 1450 else 1451 { 1452 view->timer = nullptr; 1453 } 1454 } 1455 1456 v3_cpp_obj_unref(runloop); 1457 view->runloop = nullptr; 1458 } 1459 #endif 1460 1461 view->uivst3 = nullptr; 1462 return V3_OK; 1463 } 1464 1465 static v3_result V3_API on_wheel(void* const self, const float distance) 1466 { 1467 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1468 d_debug("dpf_plugin_view::on_wheel => %p %f", self, distance); 1469 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1470 1471 UIVst3* const uivst3 = view->uivst3; 1472 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); 1473 1474 return uivst3->onWheel(distance); 1475 #else 1476 return V3_NOT_IMPLEMENTED; 1477 // unused 1478 (void)self; (void)distance; 1479 #endif 1480 } 1481 1482 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) 1483 { 1484 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1485 d_debug("dpf_plugin_view::on_key_down => %p %i %i %i", self, key_char, key_code, modifiers); 1486 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1487 1488 UIVst3* const uivst3 = view->uivst3; 1489 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); 1490 1491 return uivst3->onKeyDown(key_char, key_code, modifiers); 1492 #else 1493 return V3_NOT_IMPLEMENTED; 1494 // unused 1495 (void)self; (void)key_char; (void)key_code; (void)modifiers; 1496 #endif 1497 } 1498 1499 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) 1500 { 1501 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1502 d_debug("dpf_plugin_view::on_key_up => %p %i %i %i", self, key_char, key_code, modifiers); 1503 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1504 1505 UIVst3* const uivst3 = view->uivst3; 1506 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); 1507 1508 return uivst3->onKeyUp(key_char, key_code, modifiers); 1509 #else 1510 return V3_NOT_IMPLEMENTED; 1511 // unused 1512 (void)self; (void)key_char; (void)key_code; (void)modifiers; 1513 #endif 1514 } 1515 1516 static v3_result V3_API get_size(void* const self, v3_view_rect* const rect) 1517 { 1518 d_debug("dpf_plugin_view::get_size => %p", self); 1519 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1520 1521 if (UIVst3* const uivst3 = view->uivst3) 1522 return uivst3->getSize(rect); 1523 1524 d_debug("dpf_plugin_view::get_size => %p | NOTE: size request before attach", self); 1525 1526 view->sizeRequestedBeforeBeingAttached = true; 1527 1528 double scaleFactor = view->scale != nullptr ? view->scale->scaleFactor : 0.0; 1529 #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) 1530 if (d_isZero(scaleFactor)) 1531 scaleFactor = 1.0; 1532 rect->right = DISTRHO_UI_DEFAULT_WIDTH * scaleFactor; 1533 rect->bottom = DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor; 1534 #else 1535 UIExporter tmpUI(nullptr, 0, view->sampleRate, 1536 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, 1537 view->instancePointer, scaleFactor); 1538 rect->right = tmpUI.getWidth(); 1539 rect->bottom = tmpUI.getHeight(); 1540 scaleFactor = tmpUI.getScaleFactor(); 1541 tmpUI.quit(); 1542 #endif 1543 1544 rect->left = rect->top = 0; 1545 #ifdef DISTRHO_OS_MAC 1546 rect->right /= scaleFactor; 1547 rect->bottom /= scaleFactor; 1548 #endif 1549 1550 return V3_OK; 1551 } 1552 1553 static v3_result V3_API on_size(void* const self, v3_view_rect* const rect) 1554 { 1555 d_debug("dpf_plugin_view::on_size => %p {%d,%d,%d,%d}", 1556 self, rect->top, rect->left, rect->right, rect->bottom); 1557 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->right > rect->left, rect->right, rect->left, V3_INVALID_ARG); 1558 DISTRHO_SAFE_ASSERT_INT2_RETURN(rect->bottom > rect->top, rect->bottom, rect->top, V3_INVALID_ARG); 1559 1560 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1561 1562 if (UIVst3* const uivst3 = view->uivst3) 1563 return uivst3->onSize(rect); 1564 1565 view->nextWidth = static_cast<uint32_t>(rect->right - rect->left); 1566 view->nextHeight = static_cast<uint32_t>(rect->bottom - rect->top); 1567 return V3_OK; 1568 } 1569 1570 static v3_result V3_API on_focus(void* const self, const v3_bool state) 1571 { 1572 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 1573 d_debug("dpf_plugin_view::on_focus => %p %u", self, state); 1574 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1575 1576 UIVst3* const uivst3 = view->uivst3; 1577 DISTRHO_SAFE_ASSERT_RETURN(uivst3 != nullptr, V3_NOT_INITIALIZED); 1578 1579 return uivst3->onFocus(state); 1580 #else 1581 return V3_NOT_IMPLEMENTED; 1582 // unused 1583 (void)self; (void)state; 1584 #endif 1585 } 1586 1587 static v3_result V3_API set_frame(void* const self, v3_plugin_frame** const frame) 1588 { 1589 d_debug("dpf_plugin_view::set_frame => %p %p", self, frame); 1590 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1591 1592 view->frame = frame; 1593 1594 if (UIVst3* const uivst3 = view->uivst3) 1595 return uivst3->setFrame(frame); 1596 1597 return V3_OK; 1598 } 1599 1600 static v3_result V3_API can_resize(void* const self) 1601 { 1602 #if DISTRHO_UI_USER_RESIZABLE 1603 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1604 1605 if (UIVst3* const uivst3 = view->uivst3) 1606 return uivst3->canResize(); 1607 1608 return V3_TRUE; 1609 #else 1610 return V3_FALSE; 1611 1612 // unused 1613 (void)self; 1614 #endif 1615 } 1616 1617 static v3_result V3_API check_size_constraint(void* const self, v3_view_rect* const rect) 1618 { 1619 d_debug("dpf_plugin_view::check_size_constraint => %p {%d,%d,%d,%d}", 1620 self, rect->top, rect->left, rect->right, rect->bottom); 1621 dpf_plugin_view* const view = *static_cast<dpf_plugin_view**>(self); 1622 1623 if (UIVst3* const uivst3 = view->uivst3) 1624 return uivst3->checkSizeConstraint(rect); 1625 1626 return V3_NOT_INITIALIZED; 1627 } 1628 }; 1629 1630 // -------------------------------------------------------------------------------------------------------------------- 1631 // dpf_plugin_view_create (called from plugin side) 1632 1633 v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate); 1634 1635 v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, 1636 void* const instancePointer, 1637 const double sampleRate) 1638 { 1639 dpf_plugin_view** const viewptr = new dpf_plugin_view*; 1640 *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate); 1641 return static_cast<v3_plugin_view**>(static_cast<void*>(viewptr)); 1642 } 1643 1644 // -------------------------------------------------------------------------------------------------------------------- 1645 1646 END_NAMESPACE_DISTRHO