From 4836546d08b791050c5d584914d63d109ca2ce5c Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Thu, 29 Sep 2022 20:11:30 +0200 Subject: [PATCH] Fully implement the CLAP GUI extension --- src/common/serialization/clap/README.md | 38 +-- .../bridges/clap-impls/plugin-proxy.cpp | 216 ++++++++++++++++++ src/plugin/bridges/clap-impls/plugin-proxy.h | 35 +++ 3 files changed, 270 insertions(+), 19 deletions(-) diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index da1971aa..df1d34ea 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -13,25 +13,25 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | `clap.plugin-factory` | :heavy_check_mark: | | `clap.plugin-invalidation-factory/draft0` | :x: Not supported yet | -| extension | status | -| ------------------------- | -------------------------------------------------- | -| `clap.audio-ports` | :heavy_check_mark: | -| `clap.audio-ports-config` | :x: Not supported yet | -| `clap.event-registry` | :x: Not needed for any of the supported extensions | -| `clap.gui` | :x: Not supported yet | -| `clap.latency` | :heavy_check_mark: | -| `clap.log` | :x: Not supported yet | -| `clap.note-name` | :x: Not supported yet | -| `clap.note-ports` | :heavy_check_mark: | -| `clap.params` | :heavy_check_mark: | -| `clap.posix-fd-support` | :x: Not supported yet | -| `clap.render` | :x: Not supported yet | -| `clap.state` | :heavy_check_mark: | -| `clap.tail` | :heavy_check_mark: | -| `clap.thread-check` | :x: Not supported yet | -| `clap.thread-pool` | :x: Not supported yet | -| `clap.timer-support` | :x: Not supported yet | -| `clap.voice-info` | :x: Not supported yet | +| extension | status | +| ------------------------- | ---------------------------------------------------- | +| `clap.audio-ports` | :heavy_check_mark: | +| `clap.audio-ports-config` | :x: Not supported yet | +| `clap.event-registry` | :x: Not needed for any of the supported extensions | +| `clap.gui` | :heavy_check_mark: Currently only does embedded GUIs | +| `clap.latency` | :heavy_check_mark: | +| `clap.log` | :x: Not supported yet | +| `clap.note-name` | :x: Not supported yet | +| `clap.note-ports` | :heavy_check_mark: | +| `clap.params` | :heavy_check_mark: | +| `clap.posix-fd-support` | :x: Not supported yet | +| `clap.render` | :x: Not supported yet | +| `clap.state` | :heavy_check_mark: | +| `clap.tail` | :heavy_check_mark: | +| `clap.thread-check` | :x: Not supported yet | +| `clap.thread-pool` | :x: Not supported yet | +| `clap.timer-support` | :x: Not supported yet | +| `clap.voice-info` | :x: Not supported yet | | draft extension | status | | -------------------------------- | --------------------- | diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.cpp b/src/plugin/bridges/clap-impls/plugin-proxy.cpp index 5285bf8b..d8a041a2 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.cpp +++ b/src/plugin/bridges/clap-impls/plugin-proxy.cpp @@ -74,6 +74,23 @@ clap_plugin_proxy::clap_plugin_proxy(ClapPluginBridge& bridge, .count = ext_audio_ports_count, .get = ext_audio_ports_get, }), + ext_gui_vtable(clap_plugin_gui_t{ + .is_api_supported = ext_gui_is_api_supported, + .get_preferred_api = ext_gui_get_preferred_api, + .create = ext_gui_create, + .destroy = ext_gui_destroy, + .set_scale = ext_gui_set_scale, + .get_size = ext_gui_get_size, + .can_resize = ext_gui_can_resize, + .get_resize_hints = ext_gui_get_resize_hints, + .adjust_size = ext_gui_adjust_size, + .set_size = ext_gui_set_size, + .set_parent = ext_gui_set_parent, + .set_transient = ext_gui_set_transient, + .suggest_title = ext_gui_suggest_title, + .show = ext_gui_show, + .hide = ext_gui_hide, + }), ext_latency_vtable(clap_plugin_latency_t{ .get = ext_latency_get, }), @@ -226,6 +243,9 @@ clap_plugin_proxy::plugin_get_extension(const struct clap_plugin* plugin, if (self->supported_extensions_.supports_audio_ports && strcmp(id, CLAP_EXT_AUDIO_PORTS) == 0) { extension_ptr = &self->ext_audio_ports_vtable; + } else if (self->supported_extensions_.supports_gui && + strcmp(id, CLAP_EXT_GUI) == 0) { + extension_ptr = &self->ext_gui_vtable; } else if (self->supported_extensions_.supports_latency && strcmp(id, CLAP_EXT_LATENCY) == 0) { extension_ptr = &self->ext_latency_vtable; @@ -296,6 +316,202 @@ clap_plugin_proxy::ext_audio_ports_get(const clap_plugin_t* plugin, } } +bool CLAP_ABI +clap_plugin_proxy::ext_gui_is_api_supported(const clap_plugin_t* plugin, + const char* api, + bool is_floating) { + assert(plugin && plugin->plugin_data && api); + auto self = static_cast(plugin->plugin_data); + + // We only support embedded X11 windows for now + if (strcmp(api, CLAP_WINDOW_API_X11) != 0 || is_floating) { + return false; + } + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::IsApiSupported{ + .instance_id = self->instance_id(), + // This will be translated to WIN32 on the Wine plugin host side + .api = clap::ext::gui::ApiType::X11, + .is_floating = is_floating}); +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_get_preferred_api(const clap_plugin_t* plugin, + const char** api, + bool* is_floating) { + assert(plugin && plugin->plugin_data && api && is_floating); + + // We only support floating X11 windows right now + *api = CLAP_WINDOW_API_X11; + *is_floating = false; + + return true; +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_create(const clap_plugin_t* plugin, + const char* api, + bool is_floating) { + assert(plugin && plugin->plugin_data && api); + auto self = static_cast(plugin->plugin_data); + + // We only support embedded X11 windows for now + if (strcmp(api, CLAP_WINDOW_API_X11) != 0 || is_floating) { + return false; + } + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::Create{ + .instance_id = self->instance_id(), + // This will be translated to WIN32 on the Wine plugin host side + .api = clap::ext::gui::ApiType::X11, + .is_floating = is_floating}); +} + +void CLAP_ABI clap_plugin_proxy::ext_gui_destroy(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::Destroy{.instance_id = self->instance_id()}); +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_set_scale(const clap_plugin_t* plugin, + double scale) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::SetScale{.instance_id = self->instance_id(), + .scale = scale}); +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_get_size(const clap_plugin_t* plugin, + uint32_t* width, + uint32_t* height) { + assert(plugin && plugin->plugin_data && width && height); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::gui::plugin::GetSizeResponse response = + self->bridge_.send_main_thread_message(clap::ext::gui::plugin::GetSize{ + .instance_id = self->instance_id()}); + + if (response.result) { + *width = response.width; + *height = response.height; + } + + return response.result; +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_can_resize(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::CanResize{.instance_id = self->instance_id()}); +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_get_resize_hints(const clap_plugin_t* plugin, + clap_gui_resize_hints_t* hints) { + assert(plugin && plugin->plugin_data && hints); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::gui::plugin::GetResizeHintsResponse response = + self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::GetResizeHints{.instance_id = + self->instance_id()}); + if (response.result) { + *hints = *response.result; + + return true; + } else { + return false; + } +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_adjust_size(const clap_plugin_t* plugin, + uint32_t* width, + uint32_t* height) { + assert(plugin && plugin->plugin_data && width && height); + auto self = static_cast(plugin->plugin_data); + + const clap::ext::gui::plugin::AdjustSizeResponse response = + self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::AdjustSize{ + .instance_id = self->instance_id(), + .width = *width, + .height = *height}); + + if (response.result) { + *width = response.updated_width; + *height = response.updated_height; + } + + return response.result; +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_set_size(const clap_plugin_t* plugin, + uint32_t width, + uint32_t height) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::SetSize{.instance_id = self->instance_id(), + .width = width, + .height = height}); +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_set_parent(const clap_plugin_t* plugin, + const clap_window_t* window) { + assert(plugin && plugin->plugin_data && window); + auto self = static_cast(plugin->plugin_data); + + // We only support X11 windows right now, so this will always be an X11 + // window + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::SetParent{.instance_id = self->instance_id(), + .x11_window = window->x11}); +} + +bool CLAP_ABI +clap_plugin_proxy::ext_gui_set_transient(const clap_plugin_t* plugin, + const clap_window_t* window) { + assert(plugin && plugin->plugin_data && window); + + // We don't support floating windows right now + return false; +} + +void CLAP_ABI +clap_plugin_proxy::ext_gui_suggest_title(const clap_plugin_t* plugin, + const char* title) { + assert(plugin && plugin->plugin_data && title); + + // We don't support floating windows right now +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_show(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::Show{.instance_id = self->instance_id()}); +} + +bool CLAP_ABI clap_plugin_proxy::ext_gui_hide(const clap_plugin_t* plugin) { + assert(plugin && plugin->plugin_data); + auto self = static_cast(plugin->plugin_data); + + return self->bridge_.send_main_thread_message( + clap::ext::gui::plugin::Hide{.instance_id = self->instance_id()}); +} + uint32_t CLAP_ABI clap_plugin_proxy::ext_latency_get(const clap_plugin_t* plugin) { assert(plugin && plugin->plugin_data); diff --git a/src/plugin/bridges/clap-impls/plugin-proxy.h b/src/plugin/bridges/clap-impls/plugin-proxy.h index b68ffd01..12fbbf6d 100644 --- a/src/plugin/bridges/clap-impls/plugin-proxy.h +++ b/src/plugin/bridges/clap-impls/plugin-proxy.h @@ -191,6 +191,40 @@ class clap_plugin_proxy { bool is_input, clap_audio_port_info_t* info); + static bool CLAP_ABI ext_gui_is_api_supported(const clap_plugin_t* plugin, + const char* api, + bool is_floating); + static bool CLAP_ABI ext_gui_get_preferred_api(const clap_plugin_t* plugin, + const char** api, + bool* is_floating); + static bool CLAP_ABI ext_gui_create(const clap_plugin_t* plugin, + const char* api, + bool is_floating); + static void CLAP_ABI ext_gui_destroy(const clap_plugin_t* plugin); + static bool CLAP_ABI ext_gui_set_scale(const clap_plugin_t* plugin, + double scale); + static bool CLAP_ABI ext_gui_get_size(const clap_plugin_t* plugin, + uint32_t* width, + uint32_t* height); + static bool CLAP_ABI ext_gui_can_resize(const clap_plugin_t* plugin); + static bool CLAP_ABI + ext_gui_get_resize_hints(const clap_plugin_t* plugin, + clap_gui_resize_hints_t* hints); + static bool CLAP_ABI ext_gui_adjust_size(const clap_plugin_t* plugin, + uint32_t* width, + uint32_t* height); + static bool CLAP_ABI ext_gui_set_size(const clap_plugin_t* plugin, + uint32_t width, + uint32_t height); + static bool CLAP_ABI ext_gui_set_parent(const clap_plugin_t* plugin, + const clap_window_t* window); + static bool CLAP_ABI ext_gui_set_transient(const clap_plugin_t* plugin, + const clap_window_t* window); + static void CLAP_ABI ext_gui_suggest_title(const clap_plugin_t* plugin, + const char* title); + static bool CLAP_ABI ext_gui_show(const clap_plugin_t* plugin); + static bool CLAP_ABI ext_gui_hide(const clap_plugin_t* plugin); + static uint32_t CLAP_ABI ext_latency_get(const clap_plugin_t* plugin); static uint32_t CLAP_ABI ext_note_ports_count(const clap_plugin_t* plugin, @@ -254,6 +288,7 @@ class clap_plugin_proxy { // depends on whether the plugin supported this extension when we called // `clap_plugin::init()`. const clap_plugin_audio_ports_t ext_audio_ports_vtable; + const clap_plugin_gui_t ext_gui_vtable; const clap_plugin_latency_t ext_latency_vtable; const clap_plugin_note_ports_t ext_note_ports_vtable; const clap_plugin_params_t ext_params_vtable;