From f944bf4a39137140789cc3e5ba898d4162f1e969 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 7 Jan 2021 16:43:13 +0100 Subject: [PATCH] Fully implement IContextMenu Although all of this stuff is completely untested, and since no host on Linux uses it we'll likely never know whether this implementation is correct. --- meson.build | 1 + src/common/serialization/vst3/README.md | 4 +- .../vst3/context-menu/context-menu.h | 2 +- .../bridges/vst3-impls/context-menu-proxy.cpp | 71 ++++++++++++++----- .../bridges/vst3-impls/context-menu-proxy.h | 10 ++- 5 files changed, 66 insertions(+), 22 deletions(-) diff --git a/meson.build b/meson.build index ddb957f7..da30d040 100644 --- a/meson.build +++ b/meson.build @@ -193,6 +193,7 @@ if with_vst3 'src/common/serialization/vst3/component-handler-proxy.cpp', 'src/common/serialization/vst3/connection-point-proxy.cpp', 'src/common/serialization/vst3/context-menu-proxy.cpp', + 'src/common/serialization/vst3/context-menu-target.cpp', 'src/common/serialization/vst3/event-list.cpp', 'src/common/serialization/vst3/host-context-proxy.cpp', 'src/common/serialization/vst3/message.cpp', diff --git a/src/common/serialization/vst3/README.md b/src/common/serialization/vst3/README.md index 71ccf1cf..d223cdf0 100644 --- a/src/common/serialization/vst3/README.md +++ b/src/common/serialization/vst3/README.md @@ -6,8 +6,8 @@ for more information on how the serialization works. The following interfaces are not yet implemented: -- Every interface introduced after VST 3.1.0 with the exception of - `INoteExpressionController` which has already been implemented +- Every interface introduced after VST 3.1.0, although most of the VST 3.5.0 + interfaces have already been implemented - The [Presonus extensions](https://presonussoftware.com/en_US/developer), although most of these things seem to overlap with newer VST3 interfaces diff --git a/src/common/serialization/vst3/context-menu/context-menu.h b/src/common/serialization/vst3/context-menu/context-menu.h index 09654c67..2c767287 100644 --- a/src/common/serialization/vst3/context-menu/context-menu.h +++ b/src/common/serialization/vst3/context-menu/context-menu.h @@ -150,7 +150,7 @@ class YaContextMenu : public Steinberg::Vst::IContextMenu { }; virtual tresult PLUGIN_API - removeItem(const Item& item, + removeItem(const Steinberg::Vst::IContextMenuItem& item, Steinberg::Vst::IContextMenuTarget* target) override = 0; /** diff --git a/src/wine-host/bridges/vst3-impls/context-menu-proxy.cpp b/src/wine-host/bridges/vst3-impls/context-menu-proxy.cpp index 636d4678..7a2ef442 100644 --- a/src/wine-host/bridges/vst3-impls/context-menu-proxy.cpp +++ b/src/wine-host/bridges/vst3-impls/context-menu-proxy.cpp @@ -45,39 +45,76 @@ Vst3ContextMenuProxyImpl::queryInterface(const Steinberg::TUID _iid, } int32 PLUGIN_API Vst3ContextMenuProxyImpl::getItemCount() { - // TODO: Implement - std::cerr << "TODO: IContextMenu::getItemCount()" << std::endl; - return 0; + return bridge.send_message( + YaContextMenu::GetItemCount{.owner_instance_id = owner_instance_id(), + .context_menu_id = context_menu_id()}); } tresult PLUGIN_API Vst3ContextMenuProxyImpl::getItem( int32 index, Steinberg::Vst::IContextMenuItem& item /*out*/, Steinberg::Vst::IContextMenuTarget** target /*out*/) { - // TODO: Implement - std::cerr << "TODO: IContextMenu::getItem()" << std::endl; - return Steinberg::kNotImplemented; + // XXX: Should the plugin be able to get targets created by the host this + // way? We'll just assume that this function won't ever be called by + // the plugin (but we'll implement a basic version anyways). + if (index < 0 || index >= static_cast(items.size())) { + return Steinberg::kInvalidArgument; + } else { + item = items[index]; + *target = context_menu_targets[item.tag]; + + return Steinberg::kResultOk; + } } tresult PLUGIN_API Vst3ContextMenuProxyImpl::addItem(const Steinberg::Vst::IContextMenuItem& item, Steinberg::Vst::IContextMenuTarget* target) { - // TODO: Implement - std::cerr << "TODO: IContextMenu::addItem()" << std::endl; - return Steinberg::kNotImplemented; + const tresult result = bridge.send_message(YaContextMenu::AddItem{ + .owner_instance_id = owner_instance_id(), + .context_menu_id = context_menu_id(), + .item = item, + .target = + (target ? std::make_optional( + owner_instance_id(), context_menu_id(), item.tag) + : std::nullopt)}); + + if (result == Steinberg::kResultOk) { + items.push_back(item); + context_menu_targets[item.tag] = target; + } + + return result; } tresult PLUGIN_API Vst3ContextMenuProxyImpl::removeItem( - const Item& item, - Steinberg::Vst::IContextMenuTarget* target) { - // TODO: Implement - std::cerr << "TODO: IContextMenu::removeItem()" << std::endl; - return Steinberg::kNotImplemented; + const Steinberg::Vst::IContextMenuItem& item, + Steinberg::Vst::IContextMenuTarget* /*target*/) { + const tresult result = bridge.send_message( + YaContextMenu::RemoveItem{.owner_instance_id = owner_instance_id(), + .context_menu_id = context_menu_id(), + .item = item}); + + if (result == Steinberg::kResultOk) { + items.erase( + std::remove_if( + items.begin(), items.end(), + [&](const Steinberg::Vst::IContextMenuItem& candidate_item) { + // They didn't implement `operator==` on the struct + return candidate_item.tag == item.tag; + }), + items.end()); + context_menu_targets.erase(item.tag); + } + + return result; } tresult PLUGIN_API Vst3ContextMenuProxyImpl::popup(Steinberg::UCoord x, Steinberg::UCoord y) { - // TODO: Implement - std::cerr << "TODO: IContextMenu::popup()" << std::endl; - return Steinberg::kNotImplemented; + return bridge.send_message( + YaContextMenu::Popup{.owner_instance_id = owner_instance_id(), + .context_menu_id = context_menu_id(), + .x = x, + .y = y}); } diff --git a/src/wine-host/bridges/vst3-impls/context-menu-proxy.h b/src/wine-host/bridges/vst3-impls/context-menu-proxy.h index 3b705d62..5e25a16c 100644 --- a/src/wine-host/bridges/vst3-impls/context-menu-proxy.h +++ b/src/wine-host/bridges/vst3-impls/context-menu-proxy.h @@ -46,17 +46,23 @@ class Vst3ContextMenuProxyImpl : public Vst3ContextMenuProxy { addItem(const Steinberg::Vst::IContextMenuItem& item, Steinberg::Vst::IContextMenuTarget* target) override; tresult PLUGIN_API - removeItem(const Item& item, + removeItem(const Steinberg::Vst::IContextMenuItem& item, Steinberg::Vst::IContextMenuTarget* target) override; tresult PLUGIN_API popup(Steinberg::UCoord x, Steinberg::UCoord y) override; /** * The targets passed when to `addItem` calls made by the plugin. This way - * we can call these same targets later. + * we can call these same targets later. The key here is the item's tag. */ std::map> context_menu_targets; private: Vst3Bridge& bridge; + + /** + * The items passed when to `addItem` calls made by the plugin. This way we + * can call these same targets later. + */ + std::vector items; };