diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index 443d7493..7f9cdc93 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -179,6 +179,7 @@ using CallbackRequest = std::variant menu) { + std::lock_guard lock(context_menus_mutex); + + const size_t context_menu_id = current_context_menu_id.fetch_add(1); + context_menus.emplace(context_menu_id, std::move(menu)); + + return context_menu_id; +} + +/** + * Unregister a context menu using the ID generated by a previous call to + * `register_context_menu()`. This will release the context menu object + * returned by the host. + */ +bool Vst3PluginProxyImpl::unregister_context_menu(size_t context_menu_id) { + std::lock_guard lock(context_menus_mutex); + return context_menus.erase(context_menu_id); +} + tresult PLUGIN_API Vst3PluginProxyImpl::setAudioPresentationLatencySamples( Steinberg::Vst::BusDirection dir, int32 busIndex, @@ -373,6 +393,7 @@ tresult PLUGIN_API Vst3PluginProxyImpl::setComponentHandler( // Automatically converted smart pointers for when the plugin performs a // callback later component_handler_2 = component_handler; + component_handler_3 = component_handler; unit_handler = component_handler; return bridge.send_message(YaEditController::SetComponentHandler{ diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.h b/src/plugin/bridges/vst3-impls/plugin-proxy.h index 4ff42cc7..c68bdffd 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.h +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.h @@ -38,6 +38,22 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { tresult PLUGIN_API queryInterface(const Steinberg::TUID _iid, void** obj) override; + /** + * Add a context menu created by a call to + * `IComponentHandler3::createContextMenu` to our list of registered cotnext + * menus. This way we can refer to it later when the plugin calls a function + * on the proxy object we'll create for it. + */ + size_t register_context_menu( + Steinberg::IPtr menu); + + /** + * Unregister a context menu using the ID generated by a previous call to + * `register_context_menu()`. This will release the context menu object + * returned by the host. + */ + bool unregister_context_menu(size_t context_menu_id); + // From `IAudioPresentationLatency` tresult PLUGIN_API setAudioPresentationLatencySamples(Steinberg::Vst::BusDirection dir, @@ -242,6 +258,21 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { */ Vst3PlugViewProxyImpl* last_created_plug_view = nullptr; + /** + * All context menus created by this object through + * `IComponentHandler3::createContextMenu()`. We'll generate a unique + * identifier for each context menu just like we do for plugin objects. When + * the plugin drops the context menu object, we'll also remove the + * corresponding entry for this map causing the original pointer returned by + * the host to get dropped a well. + * + * @see Vst3PluginProxyImpl::register_context_menu + * @see Vst3PluginProxyImpl::unregister_context_menu + */ + std::map> + context_menus; + std::mutex context_menus_mutex; + // The following pointers are cast from `host_context` if // `IPluginBase::initialize()` has been called @@ -252,6 +283,8 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { Steinberg::FUnknownPtr component_handler_2; + Steinberg::FUnknownPtr + component_handler_3; Steinberg::FUnknownPtr unit_handler; private: @@ -265,4 +298,12 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { * but for the sake of correctness we will. */ Steinberg::IPtr host_context; + + /** + * Used to assign unique identifiers to context menus created by + * `IComponentHandler3::CreateContextMenu`. + * + * @related Vst3PluginProxyImpl::register_context_menu + */ + std::atomic_size_t current_context_menu_id; }; diff --git a/src/plugin/bridges/vst3.cpp b/src/plugin/bridges/vst3.cpp index 7d9b60d6..981d2ac4 100644 --- a/src/plugin/bridges/vst3.cpp +++ b/src/plugin/bridges/vst3.cpp @@ -102,6 +102,43 @@ Vst3PluginBridge::Vst3PluginBridge() .get() .component_handler_2->finishGroupEdit(); }, + [&](const YaComponentHandler3::CreateContextMenu& request) + -> YaComponentHandler3::CreateContextMenu::Response { + // XXX: As mentioned elsewhere, since VST3 only supports a + // single plug view type at the moment we'll just + // assume that this function is called from the last + // (and only) `IPlugView*` instance returned by the + // plugin. + Vst3PlugViewProxyImpl* plug_view = + plugin_proxies.at(request.owner_instance_id) + .get() + .last_created_plug_view; + + Steinberg::IPtr context_menu = + Steinberg::owned( + plugin_proxies.at(request.owner_instance_id) + .get() + .component_handler_3->createContextMenu( + plug_view, request.param_id + ? &*request.param_id + : nullptr)); + + if (context_menu) { + const size_t context_menu_id = + plugin_proxies.at(request.owner_instance_id) + .get() + .register_context_menu(std::move(context_menu)); + + return YaComponentHandler3::CreateContextMenuResponse{ + .context_menu_args = + Vst3ContextMenuProxy::ConstructArgs( + context_menu, request.owner_instance_id, + context_menu_id)}; + } else { + return YaComponentHandler3::CreateContextMenuResponse{ + .context_menu_args = std::nullopt}; + } + }, [&](YaConnectionPoint::Notify& request) -> YaConnectionPoint::Notify::Response { return plugin_proxies.at(request.instance_id) diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 55a3932f..53e01c7e 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -353,7 +353,7 @@ class Vst3Bridge : public HostBridge { Vst3Sockets sockets; /** - * Used to assign unique identifier to instances created for + * Used to assign unique identifiers to instances created for * `IPluginFactory::createInstance()`. * * @related enerate_instance_id