From bf3d802f369d33902a31ba35b9eb4f570bbb3e20 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 19 Dec 2020 15:04:27 +0100 Subject: [PATCH] Implement IEditController::setComponentHandler() --- src/common/logging/vst3.cpp | 15 +++++++ src/common/logging/vst3.h | 2 + src/common/serialization/vst3.h | 1 + .../vst3/plugin/edit-controller.h | 31 ++++++++++++++ .../bridges/vst3-impls/plugin-proxy.cpp | 21 ++++++++-- src/plugin/bridges/vst3-impls/plugin-proxy.h | 8 ++++ src/wine-host/bridges/vst3.cpp | 42 +++++++++++++++---- src/wine-host/bridges/vst3.h | 14 ++++++- 8 files changed, 122 insertions(+), 12 deletions(-) diff --git a/src/common/logging/vst3.cpp b/src/common/logging/vst3.cpp index 51e6852d..233ea3c7 100644 --- a/src/common/logging/vst3.cpp +++ b/src/common/logging/vst3.cpp @@ -391,6 +391,21 @@ bool Vst3Logger::log_request( }); } +bool Vst3Logger::log_request( + bool is_host_vst, + const YaEditController::SetComponentHandler& request) { + return log_request_base(is_host_vst, [&](auto& message) { + message << request.instance_id + << ": IEditController::setComponentHandler(handler = "; + if (request.component_handler_proxy_args) { + message << ""; + } else { + message << ""; + } + message << ")"; + }); +} + bool Vst3Logger::log_request(bool is_host_vst, const YaPluginBase::Initialize& request) { return log_request_base(is_host_vst, [&](auto& message) { diff --git a/src/common/logging/vst3.h b/src/common/logging/vst3.h index 4f300f10..7174986a 100644 --- a/src/common/logging/vst3.h +++ b/src/common/logging/vst3.h @@ -102,6 +102,8 @@ class Vst3Logger { const YaEditController::GetParamNormalized&); bool log_request(bool is_host_vst, const YaEditController::SetParamNormalized&); + bool log_request(bool is_host_vst, + const YaEditController::SetComponentHandler&); bool log_request(bool is_host_vst, const YaPluginBase::Initialize&); bool log_request(bool is_host_vst, const YaPluginBase::Terminate&); bool log_request(bool is_host_vst, const YaPluginFactory::Construct&); diff --git a/src/common/serialization/vst3.h b/src/common/serialization/vst3.h index 7b7d0a8a..a8415102 100644 --- a/src/common/serialization/vst3.h +++ b/src/common/serialization/vst3.h @@ -87,6 +87,7 @@ using ControlRequest = std::variant #include #include "../../common.h" #include "../base.h" +#include "../component-handler-proxy.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" @@ -328,6 +330,35 @@ class YaEditController : public Steinberg::Vst::IEditController { virtual tresult PLUGIN_API setParamNormalized(Steinberg::Vst::ParamID id, Steinberg::Vst::ParamValue value) override = 0; + + /** + * Message to pass through a call to + * `IEditController::setComponentHandler(handler)` to the Wine plugin host. + * Like when creating a proxy for a plugin object, we'll read all supported + * interfaces form the component handler instance passed by the host. We'll + * then create a perfect proxy on the plugin side, that can do callbacks to + * the actual component handler passed by the host. + */ + struct SetComponentHandler { + using Response = UniversalTResult; + + native_size_t instance_id; + + /** + * Arguments for instantiating the proxy object. Even though it should + * never happen, if the host passed a null pointer to this function + * we'll mimic that as well. + */ + std::optional + component_handler_proxy_args; + + template + void serialize(S& s) { + s.value8b(instance_id); + s.ext(component_handler_proxy_args, bitsery::ext::StdOptional{}); + } + }; + virtual tresult PLUGIN_API setComponentHandler( Steinberg::Vst::IComponentHandler* handler) override = 0; virtual Steinberg::IPlugView* PLUGIN_API diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp index 8405dd35..5cd99202 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.cpp @@ -315,9 +315,24 @@ Vst3PluginProxyImpl::setParamNormalized(Steinberg::Vst::ParamID id, tresult PLUGIN_API Vst3PluginProxyImpl::setComponentHandler( Steinberg::Vst::IComponentHandler* handler) { - // TODO: Implement - bridge.logger.log("TODO IEditController::setComponentHandler()"); - return Steinberg::kNotImplemented; + std::optional + component_handler_proxy_args = std::nullopt; + if (handler) { + // We'll store the pointer for when the plugin later makes a callback to + // this component handler + component_handler = handler; + + component_handler_proxy_args = Vst3ComponentHandlerProxy::ConstructArgs( + host_application_context, instance_id()); + } else { + bridge.logger.log( + "Null pointer passed to 'IEditController::setComponentHandler'"); + } + + return bridge.send_message(YaEditController::SetComponentHandler{ + .instance_id = instance_id(), + .component_handler_proxy_args = + std::move(component_handler_proxy_args)}); } Steinberg::IPlugView* PLUGIN_API diff --git a/src/plugin/bridges/vst3-impls/plugin-proxy.h b/src/plugin/bridges/vst3-impls/plugin-proxy.h index d4c0304c..2b5c2c25 100644 --- a/src/plugin/bridges/vst3-impls/plugin-proxy.h +++ b/src/plugin/bridges/vst3-impls/plugin-proxy.h @@ -133,4 +133,12 @@ class Vst3PluginProxyImpl : public Vst3PluginProxy { */ Steinberg::FUnknownPtr host_application_context; + + /** + * The component handler the host passed to us during + * `IEditController::setComponentHandler()`. When the plugin makes a + * callback on a component handler proxy object, we'll pass the call through + * to this object. + */ + Steinberg::IPtr component_handler; }; diff --git a/src/wine-host/bridges/vst3.cpp b/src/wine-host/bridges/vst3.cpp index 8c8c1dc9..acf09ce3 100644 --- a/src/wine-host/bridges/vst3.cpp +++ b/src/wine-host/bridges/vst3.cpp @@ -23,6 +23,7 @@ #include #include +#include "vst3-impls/component-handler-proxy.h" #include "vst3-impls/host-application.h" InstanceInterfaces::InstanceInterfaces() {} @@ -336,28 +337,55 @@ void Vst3Bridge::run() { .edit_controller->setParamNormalized(request.id, request.value); }, + [&](YaEditController::SetComponentHandler& request) + -> YaEditController::SetComponentHandler::Response { + // If we got passed a component handler, we'll create a proxy + // object and pass that to the initialize function. The lifetime + // of this object is tied to that of the actual plugin object + // we're proxying for. + // TODO: Does this have to be run from the UI thread? Figure out + // if it does + if (request.component_handler_proxy_args) { + object_instances[request.instance_id] + .component_handler_proxy = + Steinberg::owned(new Vst3ComponentHandlerProxyImpl( + *this, + std::move(*request.component_handler_proxy_args))); + } else { + object_instances[request.instance_id] + .component_handler_proxy = nullptr; + } + + return object_instances[request.instance_id] + .edit_controller->setComponentHandler( + object_instances[request.instance_id] + .component_handler_proxy); + }, [&](YaPluginBase::Initialize& request) -> YaPluginBase::Initialize::Response { // If we got passed a host context, we'll create a proxy object - // and pass that to the initialize function. This object should - // be cleaned up again during `Vst3PluginProxy::Destruct`. + // and pass that to the initialize function. The lifetime of + // this object is tied to that of the actual plugin object we're + // proxying for. // TODO: This needs changing if it turns out we need a // `Vst3HostProxy` // TODO: Does this have to be run from the UI thread? Figure out // if it does - Steinberg::FUnknown* context = nullptr; if (request.host_application_context_args) { object_instances[request.instance_id] - .hsot_application_context = + .host_application_context = Steinberg::owned(new YaHostApplicationImpl( *this, std::move(*request.host_application_context_args))); - context = object_instances[request.instance_id] - .hsot_application_context; + } else { + object_instances[request.instance_id] + .host_application_context = nullptr; } return object_instances[request.instance_id] - .plugin_base->initialize(context); + .plugin_base->initialize( + object_instances[request.instance_id] + .host_application_context); }, [&](const YaPluginBase::Terminate& request) -> YaPluginBase::Terminate::Response { diff --git a/src/wine-host/bridges/vst3.h b/src/wine-host/bridges/vst3.h index 0253d6b3..7a9b1259 100644 --- a/src/wine-host/bridges/vst3.h +++ b/src/wine-host/bridges/vst3.h @@ -40,9 +40,19 @@ struct InstanceInterfaces { /** * If the host passes an `IHostApplication` during * `IPluginBase::initialize()`, we'll store a proxy object here and then - * pass it to `plugin_base->initialize()`. + * pass it to `plugin_base->initialize()`. Will be initialized with a null + * pointer until used. */ - Steinberg::IPtr hsot_application_context; + Steinberg::IPtr host_application_context; + + /** + * After a call to `IEditController::setComponentHandler()`, we'll create a + * proxy of that component handler just like we did for the plugin object. + * When the plugin calls a function on this object, we make a callback to + * the original object provided by the host. Will be initialized with a null + * pointer until used. + */ + Steinberg::IPtr component_handler_proxy; /** * The base object we cast from.