mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +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::ext::audio_ports::plugin::Count,
|
||||
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::note_ports::plugin::Count,
|
||||
clap::ext::note_ports::plugin::Get,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "plugin.h"
|
||||
|
||||
#include <clap/ext/audio-ports.h>
|
||||
#include <clap/ext/gui.h>
|
||||
#include <clap/ext/latency.h>
|
||||
#include <clap/ext/note-ports.h>
|
||||
#include <clap/ext/params.h>
|
||||
@@ -81,9 +82,10 @@ const clap_plugin_descriptor_t* Descriptor::get() const {
|
||||
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 {
|
||||
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_note_ports, CLAP_EXT_NOTE_PORTS),
|
||||
std::pair(supports_params, CLAP_EXT_PARAMS),
|
||||
|
||||
@@ -115,6 +115,7 @@ struct Descriptor {
|
||||
struct SupportedPluginExtensions {
|
||||
// Don't forget to add new extensions to below method
|
||||
bool supports_audio_ports = false;
|
||||
bool supports_gui = false;
|
||||
bool supports_latency = false;
|
||||
bool supports_note_ports = false;
|
||||
bool supports_params = false;
|
||||
@@ -125,11 +126,12 @@ struct SupportedPluginExtensions {
|
||||
* Get a list of `<bool, extension_name>` tuples for the supported
|
||||
* 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>
|
||||
void serialize(S& s) {
|
||||
s.value1b(supports_audio_ports);
|
||||
s.value1b(supports_gui);
|
||||
s.value1b(supports_latency);
|
||||
s.value1b(supports_note_ports);
|
||||
s.value1b(supports_params);
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace fs = ghc::filesystem;
|
||||
ClapPluginExtensions::ClapPluginExtensions(const clap_plugin& plugin) noexcept
|
||||
: audio_ports(static_cast<const clap_plugin_audio_ports_t*>(
|
||||
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*>(
|
||||
plugin.get_extension(&plugin, CLAP_EXT_LATENCY))),
|
||||
note_ports(static_cast<const clap_plugin_note_ports_t*>(
|
||||
@@ -46,6 +48,7 @@ clap::plugin::SupportedPluginExtensions ClapPluginExtensions::supported()
|
||||
const noexcept {
|
||||
return clap::plugin::SupportedPluginExtensions{
|
||||
.supports_audio_ports = audio_ports != nullptr,
|
||||
.supports_gui = gui != nullptr,
|
||||
.supports_latency = latency != nullptr,
|
||||
.supports_note_ports = note_ports != nullptr,
|
||||
.supports_params = params != nullptr,
|
||||
@@ -348,6 +351,226 @@ void ClapBridge::run() {
|
||||
.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::Response {
|
||||
const auto& [instance, _] = get_instance(request.instance_id);
|
||||
|
||||
@@ -67,6 +67,7 @@ struct ClapPluginExtensions {
|
||||
clap::plugin::SupportedPluginExtensions supported() const noexcept;
|
||||
|
||||
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_note_ports_t* note_ports = nullptr;
|
||||
const clap_plugin_params_t* params = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user