diff --git a/meson.build b/meson.build index 791a3ed0..02c6204d 100644 --- a/meson.build +++ b/meson.build @@ -100,6 +100,7 @@ vst3_plugin_sources = [ 'src/common/serialization/vst3/component-handler/component-handler.cpp', 'src/common/serialization/vst3/component-handler/component-handler-2.cpp', 'src/common/serialization/vst3/component-handler/unit-handler.cpp', + 'src/common/serialization/vst3/context-menu/context-menu.cpp', 'src/common/serialization/vst3/host-context/host-application.cpp', 'src/common/serialization/vst3/plug-view/parameter-finder.cpp', 'src/common/serialization/vst3/plug-view/plug-view.cpp', @@ -120,6 +121,7 @@ vst3_plugin_sources = [ 'src/common/serialization/vst3/base.cpp', '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/event-list.cpp', 'src/common/serialization/vst3/host-context-proxy.cpp', 'src/common/serialization/vst3/message.cpp', @@ -165,6 +167,7 @@ if with_vst3 'src/common/serialization/vst3/component-handler/component-handler.cpp', 'src/common/serialization/vst3/component-handler/component-handler-2.cpp', 'src/common/serialization/vst3/component-handler/unit-handler.cpp', + 'src/common/serialization/vst3/context-menu/context-menu.cpp', 'src/common/serialization/vst3/host-context/host-application.cpp', 'src/common/serialization/vst3/plug-view/parameter-finder.cpp', 'src/common/serialization/vst3/plug-view/plug-view.cpp', @@ -185,6 +188,7 @@ if with_vst3 'src/common/serialization/vst3/base.cpp', '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/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 b491c2d7..20f32934 100644 --- a/src/common/serialization/vst3/README.md +++ b/src/common/serialization/vst3/README.md @@ -44,6 +44,8 @@ VST3 host interfaces are implemented as follows: | `YaComponentHandler` | `Vst3ComponentHandlerProxy` | `IComponentHandler` | | `YaComponentHandler2` | `Vst3ComponentHandlerProxy` | `IComponentHandler2` | | `YaUnitHandler` | `Vst3ComponentHandlerProxy` | `IUnitHandler` | +| `Vst3ContextMenuProxy` | | All of the below: | +| `YaContextMenu` | `Vst3ContextMenuProxy` | `IContextMenu` | | `Vst3PlugFrameProxy` | | All of the below: | | `YaPlugFrame` | `Vst3PlugFrameProxy` | `IPlugFrame` | diff --git a/src/common/serialization/vst3/context-menu-proxy.cpp b/src/common/serialization/vst3/context-menu-proxy.cpp new file mode 100644 index 00000000..8b210c1b --- /dev/null +++ b/src/common/serialization/vst3/context-menu-proxy.cpp @@ -0,0 +1,50 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "context-menu-proxy.h" + +Vst3ContextMenuProxy::ConstructArgs::ConstructArgs() {} + +Vst3ContextMenuProxy::ConstructArgs::ConstructArgs( + Steinberg::IPtr object, + size_t owner_instance_id) + : owner_instance_id(owner_instance_id), context_menu_args(object) {} + +Vst3ContextMenuProxy::Vst3ContextMenuProxy(const ConstructArgs&& args) + : YaContextMenu(std::move(args.context_menu_args)), + arguments(std::move(args)){FUNKNOWN_CTOR} + + Vst3ContextMenuProxy::~Vst3ContextMenuProxy() { + FUNKNOWN_DTOR +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" +IMPLEMENT_REFCOUNT(Vst3ContextMenuProxy) +#pragma GCC diagnostic pop + +tresult PLUGIN_API +Vst3ContextMenuProxy::queryInterface(Steinberg::FIDString _iid, void** obj) { + if (YaContextMenu::supported()) { + QUERY_INTERFACE(_iid, obj, Steinberg::FUnknown::iid, + Steinberg::Vst::IContextMenu) + QUERY_INTERFACE(_iid, obj, Steinberg::Vst::IContextMenu::iid, + Steinberg::Vst::IContextMenu) + } + + *obj = nullptr; + return Steinberg::kNoInterface; +} diff --git a/src/common/serialization/vst3/context-menu-proxy.h b/src/common/serialization/vst3/context-menu-proxy.h new file mode 100644 index 00000000..837efe45 --- /dev/null +++ b/src/common/serialization/vst3/context-menu-proxy.h @@ -0,0 +1,122 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include "../common.h" +#include "context-menu/context-menu.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +/** + * An abstract class that implements `IContextMenu`, and optionally also all + * other VST3 interfaces an object returned by + * `IComponentHandler3::createContextMenu()` might implement. This is used to + * provide a proxy for the context menu object created by the host. The host + * will return a (prepopulated, although that's invisible to the plugin) context + * menu for right clicking on a specific parameter. The plugin can then add + * their own items to it, and then have it appear at the specified coordinates. + * Those items passed by the plugin contain callbacks that will be called when + * the user clicks on them. As far as I'm aware, not a single Linux VST3 host + * implements `IComponentHandler3` and thus provides support for these context + * menus. + * + * NOTE: For simplicity's sake (and because this is going to be true 100% of the + * time) we'll assume a plugin can only have a single context menu open at + * a time. If a host does allow creating multiple context menus for + * different parameters at the same time, then we'll just have to add a + * unique instance ID to the context menu. + */ +class Vst3ContextMenuProxy : public YaContextMenu { + public: + /** + * These are the arguments for constructing a `Vst3ContextMenuProxyImpl`. + */ + struct ConstructArgs { + ConstructArgs(); + + /** + * Read from an existing object. We will try to mimic this object, so + * we'll support any interfaces this object also supports. + */ + ConstructArgs(Steinberg::IPtr object, + size_t owner_instance_id); + + /** + * The unique instance identifier of the proxy object instance this + * context menu has been created for. + */ + native_size_t owner_instance_id; + + YaContextMenu::ConstructArgs context_menu_args; + + template + void serialize(S& s) { + s.value8b(owner_instance_id); + s.object(context_menu_args); + } + }; + + /** + * Instantiate this instance with arguments read from an actual component + * handler. + * + * This object is created as part of + * `IComponentHandler3::createContextMenu`, so there's no direct `Construct` + * message. When the object's reference count reaches zero, we should + * destroy the actual context menu object provided by the host using the + * `Destruct` message. + */ + Vst3ContextMenuProxy(const ConstructArgs&& args); + + /** + * Message to request the plugin to drop the the `IContextMenu*` returned by + * the host for the plugin instance with the given instance ID. Sent from + * the destructor of `Vst3ContextMenuProxyImpl`. + */ + struct Destruct { + using Response = Ack; + + native_size_t owner_instance_id; + + template + void serialize(S& s) { + s.value8b(owner_instance_id); + } + }; + + /** + * When this object gets dropped, we sent a `Destruct` message to also drop + * the pointer to the actual `IContextMenu*` returend by the host during + * `IComponentHandler3::createContextMenu`. + */ + virtual ~Vst3ContextMenuProxy() = 0; + + DECLARE_FUNKNOWN_METHODS + + /** + * Get the instance ID of the owner of this object. + */ + inline size_t owner_instance_id() const { + return arguments.owner_instance_id; + } + + private: + ConstructArgs arguments; +}; + +#pragma GCC diagnostic pop diff --git a/src/common/serialization/vst3/context-menu/context-menu.cpp b/src/common/serialization/vst3/context-menu/context-menu.cpp new file mode 100644 index 00000000..4d80cdf5 --- /dev/null +++ b/src/common/serialization/vst3/context-menu/context-menu.cpp @@ -0,0 +1,26 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "context-menu.h" + +YaContextMenu::ConstructArgs::ConstructArgs() {} + +YaContextMenu::ConstructArgs::ConstructArgs( + Steinberg::IPtr object) + : supported(Steinberg::FUnknownPtr(object)) {} + +YaContextMenu::YaContextMenu(const ConstructArgs&& args) + : arguments(std::move(args)) {} diff --git a/src/common/serialization/vst3/context-menu/context-menu.h b/src/common/serialization/vst3/context-menu/context-menu.h new file mode 100644 index 00000000..fabf9eee --- /dev/null +++ b/src/common/serialization/vst3/context-menu/context-menu.h @@ -0,0 +1,82 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020-2021 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#include "../../common.h" +#include "../base.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +/** + * Wraps around `IContextMenu` for serialization purposes. This is instantiated + * as part of `Vst3ContextMenuProxy`. + */ +class YaContextMenu : public Steinberg::Vst::IContextMenu { + public: + /** + * These are the arguments for creating a `YaContextMenu`. + */ + struct ConstructArgs { + ConstructArgs(); + + /** + * Check whether an existing implementation implements `IContextMenu` + * and read arguments from it. + */ + ConstructArgs(Steinberg::IPtr object); + + /** + * Whether the object supported this interface. + */ + bool supported; + + template + void serialize(S& s) { + s.value1b(supported); + } + }; + + /** + * Instantiate this instance with arguments read from another interface + * implementation. + */ + YaContextMenu(const ConstructArgs&& args); + + inline bool supported() const { return arguments.supported; } + + virtual int32 PLUGIN_API getItemCount() override = 0; + virtual tresult PLUGIN_API + getItem(int32 index, + Steinberg::Vst::IContextMenuItem& item /*out*/, + Steinberg::Vst::IContextMenuTarget** target /*out*/) override = 0; + virtual tresult PLUGIN_API + addItem(const Steinberg::Vst::IContextMenuItem& item, + Steinberg::Vst::IContextMenuTarget* target) override = 0; + virtual tresult PLUGIN_API + removeItem(const Item& item, + Steinberg::Vst::IContextMenuTarget* target) override = 0; + virtual tresult PLUGIN_API popup(Steinberg::UCoord x, + Steinberg::UCoord y) override = 0; + + protected: + ConstructArgs arguments; +}; + +#pragma GCC diagnostic pop