DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

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