clack_plugin/extensions.rs
1//! Traits and associated utilities to handle and implement CLAP extensions.
2//!
3//! These traits are designed to be used for *implementing* custom or unsupported extensions.
4//! If you want to use an existing extension in your plugin, see the `clack_extensions`
5//! crate instead.
6//!
7//! # Example
8//!
9//! This example shows a basic implementation for the plugin side of the CLAP State extension.
10//!
11//! The implementation wrapper leverages the [`PluginWrapper`](wrapper::PluginWrapper)
12//! utility to handle things like error management and unwind safety. See its documentation for more
13//! information.
14//!
15//! ```
16//! use std::ffi::CStr;
17//! use clap_sys::ext::state::{CLAP_EXT_STATE, clap_plugin_state};
18//! use clack_plugin::extensions::prelude::*;
19//!
20//! // The struct end-users will actually interact with.
21//! #[derive(Copy, Clone)]
22//! pub struct PluginState(RawExtension<PluginExtensionSide, clap_plugin_state>);
23//!
24//! unsafe impl Extension for PluginState {
25//! const IDENTIFIERS: &[&CStr] = &[CLAP_EXT_STATE];
26//! type ExtensionSide = PluginExtensionSide;
27//!
28//! #[inline]
29//! unsafe fn from_raw(raw: RawExtension<Self::ExtensionSide>) -> Self {
30//! Self(raw.cast())
31//! }
32//! }
33//!
34//! // For implementors of the extensions (here, on the plugin side):
35//! // first define a trait the extension has to implement
36//! use clack_common::stream::{InputStream, OutputStream};
37//! use clack_plugin::plugin::{Plugin, PluginError};
38//!
39//! pub trait PluginStateImplementation {
40//! fn load(&self, input: &mut InputStream) -> Result<(), PluginError>;
41//! }
42//!
43//! // Then, implement the ExtensionImplementation trait for the given implementors
44//! // to provide the C FFI-compatible struct.
45//!
46//! // SAFETY: The given struct is the CLAP extension struct for the matching side of this extension.
47//! unsafe impl<P: Plugin> ExtensionImplementation<P> for PluginState
48//! where
49//! // In this case, all the CLAP State methods belong to the main thread.
50//! // Other extensions may have other requirements, possibly split between multiple threads.
51//! for<'a> P::MainThread<'a>: PluginStateImplementation,
52//! {
53//! const IMPLEMENTATION: RawExtensionImplementation =
54//! RawExtensionImplementation::new(&clap_plugin_state {
55//! # save: Some(save),
56//! // For the sake of this example, we are only implementing the load() method.
57//! load: Some(load::<P>),
58//! });
59//! }
60//! # unsafe extern "C" fn save(_: *const clap_plugin, _: *const clap_sys::stream::clap_ostream) -> bool {
61//! # unimplemented!()
62//! # }
63//!
64//! // Finally, implement the C FFI functions that will be exposed to the host.
65//! use clap_sys::stream::clap_istream;
66//!
67//! unsafe extern "C" fn load<P: Plugin>(
68//! plugin: *const clap_plugin,
69//! stream: *const clap_istream,
70//! ) -> bool
71//! where
72//! for<'a> P::MainThread<'a>: PluginStateImplementation,
73//! {
74//! PluginWrapper::<P>::handle(plugin, |p| {
75//! let input = InputStream::from_raw_mut(&mut *(stream as *mut _));
76//! // Retrieve the plugin's main thread struct, and call load() on it
77//! p.main_thread().load(input)?;
78//! Ok(())
79//! })
80//! .is_some()
81//! }
82//! ```
83//!
84//!
85
86use crate::plugin::Plugin;
87use core::ffi::c_void;
88use std::ffi::CStr;
89use std::marker::PhantomData;
90use std::ptr::NonNull;
91
92pub mod wrapper;
93
94pub use clack_common::extensions::*;
95
96/// A collection of all extensions supported for a given plugin type `P`.
97///
98/// Plugins can declare the different extensions they support by using the
99/// [`register`](PluginExtensions::register) method on this struct, during a call to
100/// [`declare_extensions`](Plugin::declare_extensions).
101pub struct PluginExtensions<'a, P: ?Sized> {
102 found: Option<NonNull<c_void>>,
103 requested: &'a CStr,
104 plugin_type: PhantomData<P>,
105}
106
107impl<'a, P: Plugin> PluginExtensions<'a, P> {
108 #[inline]
109 pub(crate) fn new(requested: &'a CStr) -> Self {
110 Self {
111 found: None,
112 requested,
113 plugin_type: PhantomData,
114 }
115 }
116
117 #[inline]
118 pub(crate) fn found(&self) -> *const c_void {
119 self.found
120 .map(|p| p.as_ptr())
121 .unwrap_or(core::ptr::null_mut())
122 }
123
124 /// Adds a given extension implementation to the list of extensions this plugin supports.
125 #[inline]
126 pub fn register<E: ExtensionImplementation<P, ExtensionSide = PluginExtensionSide>>(
127 &mut self,
128 ) -> &mut Self {
129 if self.found.is_some() {
130 return self;
131 }
132
133 if E::IDENTIFIERS.contains(&self.requested) {
134 self.found = Some(E::IMPLEMENTATION.as_ptr())
135 }
136
137 self
138 }
139}
140
141/// A prelude which re-exports all the types and traits used for custom extension implementation.
142///
143/// See the [module docs](self) for more information on how to implement custom extensions in a plugin.
144pub mod prelude {
145 pub use crate::{
146 extensions::wrapper::{PluginWrapper, PluginWrapperError},
147 extensions::{
148 Extension, ExtensionImplementation, HostExtensionSide, PluginExtensionSide,
149 RawExtension, RawExtensionImplementation,
150 },
151 host::{HostAudioProcessorHandle, HostMainThreadHandle, HostSharedHandle},
152 plugin::{Plugin, PluginError},
153 utils::ClapId,
154 };
155 pub use clap_sys::plugin::clap_plugin;
156}