mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-09 20:29:10 +02:00
Implement the plugin side of the GUI functions
This commit is contained in:
@@ -56,6 +56,18 @@ using ClapMainThreadControlRequest =
|
|||||||
clap::plugin::Deactivate,
|
clap::plugin::Deactivate,
|
||||||
clap::ext::audio_ports::plugin::Count,
|
clap::ext::audio_ports::plugin::Count,
|
||||||
clap::ext::audio_ports::plugin::Get,
|
clap::ext::audio_ports::plugin::Get,
|
||||||
|
clap::ext::gui::plugin::IsApiSupported,
|
||||||
|
clap::ext::gui::plugin::Create,
|
||||||
|
clap::ext::gui::plugin::Destroy,
|
||||||
|
clap::ext::gui::plugin::SetScale,
|
||||||
|
clap::ext::gui::plugin::GetSize,
|
||||||
|
clap::ext::gui::plugin::CanResize,
|
||||||
|
clap::ext::gui::plugin::GetResizeHints,
|
||||||
|
clap::ext::gui::plugin::AdjustSize,
|
||||||
|
clap::ext::gui::plugin::SetSize,
|
||||||
|
clap::ext::gui::plugin::SetParent,
|
||||||
|
clap::ext::gui::plugin::Show,
|
||||||
|
clap::ext::gui::plugin::Hide,
|
||||||
clap::ext::latency::plugin::Get,
|
clap::ext::latency::plugin::Get,
|
||||||
clap::ext::note_ports::plugin::Count,
|
clap::ext::note_ports::plugin::Count,
|
||||||
clap::ext::note_ports::plugin::Get,
|
clap::ext::note_ports::plugin::Get,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
|
||||||
#include <clap/ext/audio-ports.h>
|
#include <clap/ext/audio-ports.h>
|
||||||
|
#include <clap/ext/gui.h>
|
||||||
#include <clap/ext/latency.h>
|
#include <clap/ext/latency.h>
|
||||||
#include <clap/ext/note-ports.h>
|
#include <clap/ext/note-ports.h>
|
||||||
#include <clap/ext/params.h>
|
#include <clap/ext/params.h>
|
||||||
@@ -81,9 +82,10 @@ const clap_plugin_descriptor_t* Descriptor::get() const {
|
|||||||
return &clap_descriptor;
|
return &clap_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<std::pair<bool, const char*>, 6> SupportedPluginExtensions::list()
|
std::array<std::pair<bool, const char*>, 7> SupportedPluginExtensions::list()
|
||||||
const noexcept {
|
const noexcept {
|
||||||
return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS),
|
return {std::pair(supports_audio_ports, CLAP_EXT_AUDIO_PORTS),
|
||||||
|
std::pair(supports_gui, CLAP_EXT_GUI),
|
||||||
std::pair(supports_latency, CLAP_EXT_LATENCY),
|
std::pair(supports_latency, CLAP_EXT_LATENCY),
|
||||||
std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS),
|
std::pair(supports_note_ports, CLAP_EXT_NOTE_PORTS),
|
||||||
std::pair(supports_params, CLAP_EXT_PARAMS),
|
std::pair(supports_params, CLAP_EXT_PARAMS),
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ struct Descriptor {
|
|||||||
struct SupportedPluginExtensions {
|
struct SupportedPluginExtensions {
|
||||||
// Don't forget to add new extensions to below method
|
// Don't forget to add new extensions to below method
|
||||||
bool supports_audio_ports = false;
|
bool supports_audio_ports = false;
|
||||||
|
bool supports_gui = false;
|
||||||
bool supports_latency = false;
|
bool supports_latency = false;
|
||||||
bool supports_note_ports = false;
|
bool supports_note_ports = false;
|
||||||
bool supports_params = false;
|
bool supports_params = false;
|
||||||
@@ -125,11 +126,12 @@ struct SupportedPluginExtensions {
|
|||||||
* Get a list of `<bool, extension_name>` tuples for the supported
|
* Get a list of `<bool, extension_name>` tuples for the supported
|
||||||
* extensions. Used during logging.
|
* extensions. Used during logging.
|
||||||
*/
|
*/
|
||||||
std::array<std::pair<bool, const char*>, 6> list() const noexcept;
|
std::array<std::pair<bool, const char*>, 7> list() const noexcept;
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
void serialize(S& s) {
|
void serialize(S& s) {
|
||||||
s.value1b(supports_audio_ports);
|
s.value1b(supports_audio_ports);
|
||||||
|
s.value1b(supports_gui);
|
||||||
s.value1b(supports_latency);
|
s.value1b(supports_latency);
|
||||||
s.value1b(supports_note_ports);
|
s.value1b(supports_note_ports);
|
||||||
s.value1b(supports_params);
|
s.value1b(supports_params);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ namespace fs = ghc::filesystem;
|
|||||||
ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept
|
ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept
|
||||||
: audio_ports(static_cast<const clap_plugin_audio_ports_t*>(
|
: audio_ports(static_cast<const clap_plugin_audio_ports_t*>(
|
||||||
plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS))),
|
plugin.get_extension(&plugin, CLAP_EXT_AUDIO_PORTS))),
|
||||||
|
gui(static_cast<const clap_plugin_gui_t*>(
|
||||||
|
plugin.get_extension(&plugin, CLAP_EXT_GUI))),
|
||||||
latency(static_cast<const clap_plugin_latency_t*>(
|
latency(static_cast<const clap_plugin_latency_t*>(
|
||||||
plugin.get_extension(&plugin, CLAP_EXT_LATENCY))),
|
plugin.get_extension(&plugin, CLAP_EXT_LATENCY))),
|
||||||
note_ports(static_cast<const clap_plugin_note_ports_t*>(
|
note_ports(static_cast<const clap_plugin_note_ports_t*>(
|
||||||
@@ -46,6 +48,7 @@ clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported()
|
|||||||
const noexcept {
|
const noexcept {
|
||||||
return clap::plugin::SupportedPluginExtensions{
|
return clap::plugin::SupportedPluginExtensions{
|
||||||
.supports_audio_ports = audio_ports != nullptr,
|
.supports_audio_ports = audio_ports != nullptr,
|
||||||
|
.supports_gui = gui != nullptr,
|
||||||
.supports_latency = latency != nullptr,
|
.supports_latency = latency != nullptr,
|
||||||
.supports_note_ports = note_ports != nullptr,
|
.supports_note_ports = note_ports != nullptr,
|
||||||
.supports_params = params != nullptr,
|
.supports_params = params != nullptr,
|
||||||
@@ -348,6 +351,226 @@ void ClapBridge::run() {
|
|||||||
.result = std::nullopt};
|
.result = std::nullopt};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::IsApiSupported& request)
|
||||||
|
-> clap::ext::gui::plugin::IsApiSupported::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
// It's a bit unnecessary to bridge the entire
|
||||||
|
// `is_api_supported()` function since we'll only bridge
|
||||||
|
// a single config (non-floating, X11), but this is
|
||||||
|
// makes it easier to expand in the future. The X11 API
|
||||||
|
// type gets translated to WIN32 for the plugin. We also
|
||||||
|
// prematurely return false when `is_floating` is false
|
||||||
|
// because we cannot set the transient window correctly
|
||||||
|
// when the plugin opens its own Wine window.
|
||||||
|
switch (request.api) {
|
||||||
|
case clap::ext::gui::ApiType::X11:
|
||||||
|
return gui->is_api_supported(
|
||||||
|
plugin, CLAP_WINDOW_API_WIN32,
|
||||||
|
request.is_floating);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::Create& request)
|
||||||
|
-> clap::ext::gui::plugin::Create::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
// We don't need to do anything here yet. The actual
|
||||||
|
// window is created at the final `.set_parent()` call.
|
||||||
|
// Like the above function, we'll translate the API type
|
||||||
|
// and `is_floating` will always be `false`.
|
||||||
|
switch (request.api) {
|
||||||
|
case clap::ext::gui::ApiType::X11:
|
||||||
|
return gui->create(plugin,
|
||||||
|
CLAP_WINDOW_API_WIN32,
|
||||||
|
request.is_floating);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::Destroy& request)
|
||||||
|
-> clap::ext::gui::plugin::Destroy::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui,
|
||||||
|
&editor = instance.editor]() {
|
||||||
|
gui->destroy(plugin);
|
||||||
|
|
||||||
|
// Cleanup is handled through RAII
|
||||||
|
editor.reset();
|
||||||
|
|
||||||
|
return Ack{};
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](clap::ext::gui::plugin::SetScale& request)
|
||||||
|
-> clap::ext::gui::plugin::SetScale::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
return gui->set_scale(plugin, request.scale);
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::GetSize& request)
|
||||||
|
-> clap::ext::gui::plugin::GetSize::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
uint32_t width{};
|
||||||
|
uint32_t height{};
|
||||||
|
const bool result =
|
||||||
|
gui->get_size(plugin, &width, &height);
|
||||||
|
|
||||||
|
return clap::ext::gui::plugin::GetSizeResponse{
|
||||||
|
.result = result, .width = width, .height = height};
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](clap::ext::gui::plugin::CanResize& request)
|
||||||
|
-> clap::ext::gui::plugin::CanResize::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
return gui->can_resize(plugin);
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::GetResizeHints& request)
|
||||||
|
-> clap::ext::gui::plugin::GetResizeHints::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
clap_gui_resize_hints_t hints{};
|
||||||
|
if (gui->get_resize_hints(plugin, &hints)) {
|
||||||
|
return clap::ext::gui::plugin::
|
||||||
|
GetResizeHintsResponse{.result =
|
||||||
|
std::move(hints)};
|
||||||
|
} else {
|
||||||
|
return clap::ext::gui::plugin::
|
||||||
|
GetResizeHintsResponse{.result = std::nullopt};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::AdjustSize& request)
|
||||||
|
-> clap::ext::gui::plugin::AdjustSize::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
uint32_t width = request.width;
|
||||||
|
uint32_t height = request.height;
|
||||||
|
const bool result =
|
||||||
|
gui->adjust_size(plugin, &width, &height);
|
||||||
|
|
||||||
|
return clap::ext::gui::plugin::AdjustSizeResponse{
|
||||||
|
.result = result,
|
||||||
|
.updated_width = width,
|
||||||
|
.updated_height = height};
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::SetSize& request)
|
||||||
|
-> clap::ext::gui::plugin::SetSize::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
return gui->set_size(plugin, request.width,
|
||||||
|
request.height);
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::SetParent& request)
|
||||||
|
-> clap::ext::gui::plugin::SetParent::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui,
|
||||||
|
&editor = instance.editor]() {
|
||||||
|
Editor& editor_instance =
|
||||||
|
editor.emplace(main_context_, config_,
|
||||||
|
generic_logger_, request.x11_window);
|
||||||
|
|
||||||
|
const clap_window_t window{
|
||||||
|
.api = CLAP_WINDOW_API_WIN32,
|
||||||
|
.win32 = editor_instance.get_win32_handle()};
|
||||||
|
const bool result = gui->set_parent(plugin, &window);
|
||||||
|
|
||||||
|
// Set the window's initial size according to what the
|
||||||
|
// plugin reports. Otherwise get rid of the editor again
|
||||||
|
// if the plugin didn't embed itself in it.
|
||||||
|
if (result) {
|
||||||
|
uint32_t width{};
|
||||||
|
uint32_t height{};
|
||||||
|
if (gui->get_size(plugin, &width, &height)) {
|
||||||
|
editor->resize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: There's zero reason why the window couldn't
|
||||||
|
// already be visible from the start, but
|
||||||
|
// Waves V13 VST3 plugins think it would be a
|
||||||
|
// splendid idea to randomly dereference null
|
||||||
|
// pointers when the window is already
|
||||||
|
// visible. Thanks Waves. We'll do the same
|
||||||
|
// thing for CLAP plugins just to be safe
|
||||||
|
editor->show();
|
||||||
|
} else {
|
||||||
|
editor.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::Show& request)
|
||||||
|
-> clap::ext::gui::plugin::Show::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
// We don't need any special handling for our editor window, but
|
||||||
|
// the plugin may use these functions to suspend drawing or stop
|
||||||
|
// other tasks while the window is hdden
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
return gui->show(plugin);
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
|
[&](const clap::ext::gui::plugin::Hide& request)
|
||||||
|
-> clap::ext::gui::plugin::Hide::Response {
|
||||||
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|
||||||
|
return main_context_
|
||||||
|
.run_in_context([&, plugin = instance.plugin.get(),
|
||||||
|
gui = instance.extensions.gui]() {
|
||||||
|
return gui->hide(plugin);
|
||||||
|
})
|
||||||
|
.get();
|
||||||
|
},
|
||||||
[&](clap::ext::latency::plugin::Get& request)
|
[&](clap::ext::latency::plugin::Get& request)
|
||||||
-> clap::ext::latency::plugin::Get::Response {
|
-> clap::ext::latency::plugin::Get::Response {
|
||||||
const auto& [instance, _] = get_instance(request.instance_id);
|
const auto& [instance, _] = get_instance(request.instance_id);
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ struct ClapPluginExtensions {
|
|||||||
clap::plugin::SupportedPluginExtensions supported() const noexcept;
|
clap::plugin::SupportedPluginExtensions supported() const noexcept;
|
||||||
|
|
||||||
const clap_plugin_audio_ports_t* audio_ports = nullptr;
|
const clap_plugin_audio_ports_t* audio_ports = nullptr;
|
||||||
|
const clap_plugin_gui_t* gui = nullptr;
|
||||||
const clap_plugin_latency_t* latency = nullptr;
|
const clap_plugin_latency_t* latency = nullptr;
|
||||||
const clap_plugin_note_ports_t* note_ports = nullptr;
|
const clap_plugin_note_ports_t* note_ports = nullptr;
|
||||||
const clap_plugin_params_t* params = nullptr;
|
const clap_plugin_params_t* params = nullptr;
|
||||||
|
|||||||
Reference in New Issue
Block a user