diff --git a/src/common/serialization/clap/README.md b/src/common/serialization/clap/README.md index 62edec2f..a509462a 100644 --- a/src/common/serialization/clap/README.md +++ b/src/common/serialization/clap/README.md @@ -9,7 +9,7 @@ Yabridge currently tracks CLAP 1.1.1. The implementation status for CLAP's core | core feature | status | | ----------------------------------------- | --------------------- | | Core plugin and host functionality | :x: Work in progress | -| `clap.plugin-factory` | :x: Work in progress | +| `clap.plugin-factory` | :heavy_check_mark: | | `clap.plugin-invalidation-factory/draft0` | :x: Not supported yet | | extension | status | diff --git a/src/common/serialization/clap/plugin.cpp b/src/common/serialization/clap/plugin.cpp new file mode 100644 index 00000000..8485fc12 --- /dev/null +++ b/src/common/serialization/clap/plugin.cpp @@ -0,0 +1,80 @@ +// yabridge: a Wine plugin bridge +// Copyright (C) 2020-2022 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 "plugin.h" + +namespace clap { +namespace plugin { + +descriptor::descriptor(const clap_plugin_descriptor_t& original) + : clap_version(original.clap_version), + id((assert(original.id), original.id)), + name((assert(original.id), original.id)), + vendor(original.vendor ? std::optional(original.vendor) : std::nullopt), + url(original.url ? std::optional(original.url) : std::nullopt), + manual_url(original.manual_url ? std::optional(original.manual_url) + : std::nullopt), + support_url(original.support_url ? std::optional(original.support_url) + : std::nullopt), + version(original.version ? std::optional(original.version) + : std::nullopt), + description(original.description ? std::optional(original.description) + : std::nullopt) { + // The features array is stored as an envp-style null terminated array + const char** orig_features = original.features; + if (orig_features) { + while (*orig_features) { + features.push_back(*orig_features); + orig_features++; + } + } +} + +clap_plugin_descriptor_t descriptor::get() const { + // This should be the minimum of yabridge's supported CLAP version and + // the plugin's supported CLAP version + clap_version_t supported_clap_version = clap_version; + if (CLAP_VERSION_MAJOR < clap_version.major || + (clap_version.major == CLAP_VERSION_MAJOR && + (CLAP_VERSION_MINOR < clap_version.minor || + (clap_version.minor == CLAP_VERSION_MINOR && + CLAP_VERSION_REVISION < clap_version.revision)))) { + supported_clap_version = CLAP_VERSION; + } + + // `features_ptrs` needs to be populated as envp-style null terminated array + features_ptrs.resize(features.size() + 1); + for (size_t i = 0; i < features.size(); i++) { + features_ptrs[i] = features[i].c_str(); + } + features_ptrs[features.size()] = nullptr; + + return clap_plugin_descriptor_t{ + .clap_version = supported_clap_version, + .id = id.c_str(), + .name = name.c_str(), + .vendor = vendor ? vendor->c_str() : nullptr, + .url = url ? url->c_str() : nullptr, + .manual_url = manual_url ? manual_url->c_str() : nullptr, + .support_url = support_url ? support_url->c_str() : nullptr, + .version = version ? version->c_str() : nullptr, + .description = description ? description->c_str() : nullptr, + .features = features_ptrs.data(), + }; +} + +} // namespace plugin +} // namespace clap diff --git a/src/common/serialization/clap/plugin.h b/src/common/serialization/clap/plugin.h new file mode 100644 index 00000000..23fa8ac9 --- /dev/null +++ b/src/common/serialization/clap/plugin.h @@ -0,0 +1,111 @@ +// yabridge: a Wine plugin bridge +// Copyright (C) 2020-2022 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 +#include + +#include +#include + +#include "../../bitsery/ext/in-place-optional.h" + +// Serialization messages for `clap/plugin.h` + +namespace clap { +namespace plugin { + +/** + * Owned wrapper around `clap_plugin_descriptor` for serialization purposes. + */ +struct descriptor { + /** + * Parse a plugin-provided descriptor so it can be serialized and sent to + * the native CLAP plugin. + */ + descriptor(const clap_plugin_descriptor_t& original); + + /** + * Default constructor for bitsery. + */ + descriptor() {} + + /** + * We'll report the maximum of the plugin's supported CLAP version and + * yabridge's supported CLAP version. I don't know why there's a version + * field here when the entry point also has a version field. + */ + clap_version_t clap_version; + + std::string id; + std::string name; + std::optional vendor; + std::optional url; + std::optional manual_url; + std::optional support_url; + std::optional version; + std::optional description; + + std::vector features; + + /** + * Create a CLAP plugin descriptor from this wrapper. This contains pointers + * to this object's fields, so this descriptor is only valid as long as this + * object is alive and doesn't get moved. + */ + clap_plugin_descriptor_t get() const; + + template + void serialize(S& s) { + s.object(clap_version); + + s.text1b(id, 4096); + s.text1b(name, 4096); + s.ext(vendor, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + s.ext(url, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + s.ext(manual_url, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + s.ext(support_url, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + s.ext(version, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + s.ext(description, bitsery::ext::InPlaceOptional(), + [](S& s, auto& v) { s.text1b(v, 4096); }); + + s.container(features, 4096, [](S& s, auto& v) { s.text1b(v, 4096); }); + } + + private: + /** + * A null terminated array of pointers to the features in `features`. + * Populated as part of `get()`. + */ + mutable std::vector features_ptrs; +}; + +} // namespace plugin +} // namespace clap + +template +void serialize(S& s, clap_version_t& version) { + s.value4b(version.major); + s.value4b(version.minor); + s.value4b(version.revision); +} diff --git a/src/plugin/meson.build b/src/plugin/meson.build index eaf5b73d..eaad64c5 100644 --- a/src/plugin/meson.build +++ b/src/plugin/meson.build @@ -77,6 +77,7 @@ if with_clap '../common/notifications.cpp', '../common/plugins.cpp', '../common/process.cpp', + '../common/serialization/clap/plugin.cpp', '../common/utils.cpp', '../include/llvm/small-vector.cpp', 'bridges/clap.cpp', diff --git a/src/wine-host/meson.build b/src/wine-host/meson.build index a124e36b..9fe04a23 100644 --- a/src/wine-host/meson.build +++ b/src/wine-host/meson.build @@ -80,6 +80,7 @@ host_sources = files( if with_clap host_sources += files( '../common/logging/clap.cpp', + '../common/serialization/clap/plugin.cpp', 'bridges/clap.cpp', ) endif