clack_host/extensions.rs
1//! The host side of the Clack extension system.
2//!
3//! The majority of the features in CLAP come from extensions, including
4//! parameters management, state loading and saving, GUI handling, and more.
5//!
6//! The goal of this system is to maximize flexibility and extensibility, while also preventing
7//! feature creep by not requiring all hosts to implement every single extension. This module
8//! provides thin wrappers that maintain those abilities while also enforcing both type safety and
9//! memory safety.
10//!
11//! This crate does not include any extension itself. All official, first-party CLAP extensions are
12//! implemented on top of it in the `clack-extensions` crate, but all extension implementations are
13//! treated as first-class citizens by both CLAP and Clack, regardless of their provenance. See the
14//! [Creating custom extensions](#creating-custom-extensions) section below for more information on
15//! how to implement third-party extensions.
16//!
17//! At instantiation time, both the plugin and the host will query each other's declared supported
18//! extensions. When one side declares supporting an extension, it will also provide an [`Extension`]
19//! object containing that extension's ABI to the other side. See the [`Extension`] type
20//! documentation for more information on how to use and store them.
21//!
22//! [`Extension`] ABIs are split in two parts: one that is exposed by the host, and one that is
23//! exposed by the plugin. For instance, for the `Latency` extension, the ABIs are named `HostLatency`
24//! and `PluginLatency` respectively.
25//!
26//! # Using extensions in a Clack host
27//!
28//! Supporting a specific extension in a CLAP host has two requirements:
29//!
30//! * Querying a plugin for its side of the ABI, and consuming it.
31//!
32//! This is the most straightforward part: once the plugin is instantiated and the host can access
33//! its [`PluginSharedHandle`](crate::plugin::PluginSharedHandle), it can use the
34//! [`PluginSharedHandle::get_extension`](crate::plugin::PluginSharedHandle::get_extension)
35//! method to query the plugin for any supported extension, and store its associated ABI.
36//!
37//! References to an Extension ABI can be shared, copied and used in any thread as long as they
38//! don't outlive the plugin instance. They are therefore most commonly stored in the host's
39//! [`HostShared`](crate::host::SharedHandler) associated type, as shown in the example below.
40//!
41//!
42//! * Implementing the host side of the ABI, and exposing it to the plugin to be queried.
43//!
44//! All extensions in Clack have at least one trait to be implemented onto a specific
45//! [`Host`](crate::host::HostHandlers) subtype ([`HostMainThread`](crate::host::MainThreadHandler),
46//! [`HostAudioProcessor`](crate::host::AudioProcessorHandler),
47//! or [`HostShared`](crate::host::SharedHandler)), depending on the thread specification of the
48//! ABI's method. For example, the `Log` extension's ABI has to be fully thread-safe, therefore
49//! the `HostLogImpl` trait has to be implemented on the [`HostShared`](crate::host::SharedHandler)
50//! type.
51//!
52//! See the [`host`](crate::host) module documentation to know more about CLAP's
53//! thread specification.
54//!
55//! Sometimes however, some ABIs expose different methods in different thread classes, leading
56//! to that many traits to be implemented on different types. For instance, the `Params` ABI
57//! exposes one thread-safe method and two that are main-thread only. Therefore, the
58//! `HostParamsImplShared` and `HostParamsImplMainThread` traits have to be implemented on the
59//! [`HostShared`](crate::host::SharedHandler) and [`HostMainThread`](crate::host::MainThreadHandler)
60//! types, respectively.
61//!
62//! Once this is all done, the host implementation can declare this extension by using the
63//! [`HostExtensions::register`](crate::host::HostExtensions::register) method in the
64//! [`Host::declare_extensions`](crate::host::HostHandlers::declare_extensions) method implementation.
65//!
66//! The fact that the right traits are implemented on the right [`Host`](crate::host::HostHandlers)
67//! associated types is automatically checked at compile time, upon calling the
68//! [`HostExtensions::register`](crate::host::HostExtensions::register) method.
69//!
70//! ## Example
71//!
72//! This example implements a host supporting the `Latency` extension.
73//!
74//! ```
75//! use std::cell::{Cell, OnceCell};
76//! use std::sync::OnceLock;
77//! use clack_host::prelude::*;
78//! use clack_extensions::latency::*;
79//!
80//! #[derive(Default)]
81//! struct MyHostShared {
82//! // Queried extension
83//! // Note this may be None even after instantiation,
84//! // in case the extension isn't supported by the plugin.
85//! latency_extension: OnceLock<Option<PluginLatency>>
86//! }
87//!
88//! impl<'a> SharedHandler<'a> for MyHostShared {
89//! // We can query the plugin's extensions as soon as the plugin starts initializing
90//! fn initializing(&self, instance: InitializingPluginHandle<'a>) {
91//! let _ = self.latency_extension.set(instance.get_extension());
92//! }
93//!
94//! /* ... */
95//! # fn request_restart(&self) { unimplemented!() }
96//! # fn request_process(&self) { unimplemented!() }
97//! # fn request_callback(&self) { unimplemented!() }
98//! }
99//!
100//! struct MyHostMainThread<'a> {
101//! shared: &'a MyHostShared,
102//! instance: OnceCell<InitializedPluginHandle<'a>>,
103//!
104//! // The latency that is sent to us by the plugin's Latency extension.
105//! latency_changed: Cell<bool>
106//! }
107//!
108//! impl<'a> MainThreadHandler<'a> for MyHostMainThread<'a> {
109//! // The plugin's instance handle is required to call extension methods.
110//! fn initialized(&self, instance: InitializedPluginHandle<'a>) {
111//! self.instance.set(instance).unwrap();
112//! }
113//! }
114//!
115//! impl<'a> HostLatencyImpl for MyHostMainThread<'a> {
116//! // This method is called by the plugin whenever its latency changed.
117//! fn changed(&self) {
118//! // Ensure that the plugin is instantiated and supports the Latency extension.
119//! if let Some(Some(_latency)) = self.shared.latency_extension.get() {
120//! self.latency_changed.set(true)
121//! }
122//! }
123//! }
124//!
125//! struct MyHost;
126//! impl HostHandlers for MyHost {
127//! type Shared<'a> = MyHostShared;
128//!
129//! type MainThread<'a> = MyHostMainThread<'a>;
130//! type AudioProcessor<'a> = ();
131//!
132//! fn declare_extensions(builder: &mut HostExtensions<Self>, shared: &Self::Shared<'_>) {
133//! builder.register::<HostLatency>();
134//! }
135//! }
136//! ```
137//!
138//! ## Example
139//!
140//! ```
141//! use clack_host::extensions::prelude::*;
142//! use clap_sys::ext::latency::{clap_host_latency, clap_plugin_latency, CLAP_EXT_LATENCY};
143//! use std::ffi::CStr;
144//!
145//! /// The type we will receive from a plugin implementing the Latency extension
146//! #[derive(Copy, Clone)]
147//! pub struct PluginLatency(RawExtension<PluginExtensionSide, clap_plugin_latency>);
148//!
149//! // Mark this type as being the plugin side of an extension, and tie it to its ID
150//! unsafe impl Extension for PluginLatency {
151//! const IDENTIFIERS: &[&CStr] = &[CLAP_EXT_LATENCY];
152//! type ExtensionSide = PluginExtensionSide;
153//!
154//! #[inline]
155//! unsafe fn from_raw(raw: RawExtension<Self::ExtensionSide>) -> Self {
156//! Self(unsafe { raw.cast() })
157//! }
158//! }
159//!
160//! /// The type we will expose to a plugin by implementing the Latency extension
161//! #[derive(Copy, Clone)]
162//! pub struct HostLatency(RawExtension<HostExtensionSide, clap_host_latency>);
163//!
164//! // Mark this type as being the host side of an extension, and tie it to its ID
165//! // SAFETY: CLAP_EXT_LATENCY is the ID of the clap_host_latency extension, defined by the CLAP spec
166//! unsafe impl Extension for HostLatency {
167//! const IDENTIFIERS: &[&CStr] = &[CLAP_EXT_LATENCY];
168//! type ExtensionSide = HostExtensionSide;
169//!
170//! #[inline]
171//! unsafe fn from_raw(raw: RawExtension<Self::ExtensionSide>) -> Self {
172//! // SAFETY: the guarantee that this pointer is of the correct type is upheld by the caller.
173//! unsafe { Self(raw.cast()) }
174//! }
175//! }
176//!
177//! // Implement calling to the plugin-side
178//! impl PluginLatency {
179//! // The `clap_plugin_latency.get` function requires to be called on the `[main-thread]`.
180//! // Therefore, we will require the `PluginMainThreadHandle` to be passed.
181//! #[inline]
182//! pub fn get(&self, plugin: &PluginMainThreadHandle) -> u32 {
183//! match plugin.use_extension(&self.0).get {
184//! None => 0,
185//! Some(get) => unsafe { get(plugin.as_raw()) },
186//! }
187//! }
188//! }
189//!
190//! /// Provides the implementation of the host-side to be called by the plugin.
191//! pub trait HostLatencyImpl {
192//! fn changed(&self);
193//! }
194//!
195//! // SAFETY: The given struct is the CLAP extension struct for the matching side of this extension.
196//! unsafe impl<H: for<'a> HostHandlers<MainThread<'a>: HostLatencyImpl>> ExtensionImplementation<H> for HostLatency
197//! {
198//! const IMPLEMENTATION: RawExtensionImplementation =
199//! RawExtensionImplementation::new(&clap_host_latency {
200//! changed: Some(changed::<H>),
201//! });
202//! }
203//!
204//! unsafe extern "C" fn changed<H: for<'a> HostHandlers<MainThread<'a>: HostLatencyImpl>>(host: *const clap_host)
205//! {
206//! HostWrapper::<H>::handle_main_thread(host, |host| {
207//! host.changed();
208//! Ok(())
209//! });
210//! }
211//! ```
212
213pub use clack_common::extensions::*;
214pub mod wrapper;
215
216/// A prelude which re-exports all the types and traits used for custom extension implementation.
217///
218/// See the [module docs](self) for more information on how to implement custom extensions in a host.
219pub mod prelude {
220 pub use crate::{
221 extensions::wrapper::{HostWrapper, HostWrapperError},
222 extensions::{
223 Extension, ExtensionImplementation, HostExtensionSide, PluginExtensionSide,
224 RawExtension, RawExtensionImplementation,
225 },
226 host::{HostError, HostHandlers},
227 plugin::{
228 InactivePluginMainThreadHandle, PluginAudioProcessorHandle, PluginMainThreadHandle,
229 PluginSharedHandle,
230 },
231 utils::ClapId,
232 };
233 pub use clap_sys::host::clap_host;
234}