Implement the plugin side of the GUI functions

This commit is contained in:
Robbert van der Helm
2022-09-29 19:26:49 +02:00
parent 48a3c76fe6
commit cd609fa90f
5 changed files with 242 additions and 2 deletions
+12
View File
@@ -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,
+3 -1
View File
@@ -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),
+3 -1
View File
@@ -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);
+223
View File
@@ -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);
+1
View File
@@ -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;