Skip to main content

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}