clack_plugin/factory/plugin.rs
1//! The plugin factory type, which is the entry point for hosts to discover and instantiate plugins.
2//!
3//! In CLAP, the Plugin Factory is the main factory type (and at the time of writing, the only
4//! stable standard one). Its purpose is to expose to the host a list of all the plugin types
5//! included in this file, and to allow the host to instantiate them.
6//!
7//! See the [`factory` module documentation](crate::factory) to learn more about factories.
8
9use super::*;
10use crate::host::HostInfo;
11use crate::plugin::{PluginDescriptor, PluginInstance};
12use clack_common::factory::plugin::PluginFactory;
13use clap_sys::factory::plugin_factory::clap_plugin_factory;
14use clap_sys::host::clap_host;
15use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor};
16use std::ffi::CStr;
17use std::ptr::NonNull;
18
19/// A wrapper around a given [`PluginFactoryImpl`] implementation.
20///
21/// This wrapper is required in order to expose a C FFI-compatible factory to the host, and is what
22/// needs to be exposed by an [`Entry`](crate::entry::Entry).
23pub struct PluginFactoryWrapper<F> {
24 inner: FactoryWrapper<clap_plugin_factory, F>,
25}
26
27impl<F: PluginFactoryImpl> PluginFactoryWrapper<F> {
28 const RAW: clap_plugin_factory = clap_plugin_factory {
29 get_plugin_count: Some(Self::get_plugin_count),
30 get_plugin_descriptor: Some(Self::get_plugin_descriptor),
31 create_plugin: Some(Self::create_plugin),
32 };
33
34 /// Wraps a given [`PluginFactoryImpl`] instance.
35 pub const fn new(factory: F) -> Self {
36 Self {
37 inner: FactoryWrapper::new(Self::RAW, factory),
38 }
39 }
40
41 /// Returns a shared reference to the wrapped [`PluginFactoryImpl`].
42 #[inline]
43 pub const fn factory(&self) -> &F {
44 self.inner.factory()
45 }
46
47 /// Returns a raw CLAP plugin factory pointer, ready to be used by the host.
48 #[inline]
49 pub fn as_raw_ptr(&self) -> *const clap_plugin_factory {
50 self.inner.as_raw_ptr()
51 }
52
53 #[allow(clippy::missing_safety_doc)]
54 unsafe extern "C" fn get_plugin_count(factory: *const clap_plugin_factory) -> u32 {
55 FactoryWrapper::<clap_plugin_factory, F>::handle(factory, |factory| {
56 Ok(factory.plugin_count())
57 })
58 .unwrap_or(0)
59 }
60
61 #[allow(clippy::missing_safety_doc)]
62 unsafe extern "C" fn get_plugin_descriptor(
63 factory: *const clap_plugin_factory,
64 index: u32,
65 ) -> *const clap_plugin_descriptor {
66 FactoryWrapper::<clap_plugin_factory, F>::handle(factory, |factory| {
67 match factory.plugin_descriptor(index) {
68 None => Ok(core::ptr::null()),
69 Some(d) => Ok(d.as_raw()),
70 }
71 })
72 .unwrap_or(core::ptr::null())
73 }
74
75 #[allow(clippy::missing_safety_doc)]
76 unsafe extern "C" fn create_plugin(
77 factory: *const clap_plugin_factory,
78 clap_host: *const clap_host,
79 plugin_id: *const std::os::raw::c_char,
80 ) -> *const clap_plugin {
81 FactoryWrapper::<clap_plugin_factory, F>::handle(factory, |factory| {
82 let plugin_id = CStr::from_ptr(plugin_id);
83 let clap_host = NonNull::new(clap_host as *mut _)
84 .ok_or(FactoryWrapperError::NulPtr("clap_host"))?;
85
86 let host_info = HostInfo::from_raw(clap_host);
87
88 match factory.create_plugin(host_info, plugin_id) {
89 None => Ok(core::ptr::null()),
90 Some(instance) => Ok(instance.into_owned_ptr()),
91 }
92 })
93 .unwrap_or(core::ptr::null())
94 }
95}
96
97// SAFETY: The returned raw implementation matches the spec for clap_plugin_factory
98unsafe impl<'a, F: PluginFactoryImpl + 'a> FactoryImplementation<'a> for PluginFactoryWrapper<F> {
99 type Factory = PluginFactory<'a>;
100 type Wrapped = F;
101
102 #[inline]
103 fn wrapper(&self) -> &FactoryWrapper<clap_plugin_factory, F> {
104 &self.inner
105 }
106}
107
108/// A Plugin Factory implementation.
109///
110/// See the [module documentation](self) to learn more about the role of a Plugin Factory.
111///
112/// # Example
113///
114/// The following example shows how to implement a basic, single-plugin factory.
115///
116/// ```
117/// use std::ffi::CStr;
118/// use clack_plugin::entry::prelude::*;
119/// use clack_plugin::prelude::*;
120///
121/// pub struct MyPlugin;
122///
123/// impl Plugin for MyPlugin {
124/// type AudioProcessor<'a> = ();
125/// type Shared<'a> = ();
126/// type MainThread<'a> = ();
127/// }
128///
129/// pub struct MyPluginFactory {
130/// plugin_descriptor: PluginDescriptor
131/// }
132///
133/// impl PluginFactoryImpl for MyPluginFactory {
134/// fn plugin_count(&self) -> u32 {
135/// 1 // We only have a single plugin
136/// }
137///
138/// fn plugin_descriptor(&self, index: u32) -> Option<&PluginDescriptor> {
139/// match index {
140/// 0 => Some(&self.plugin_descriptor),
141/// _ => None
142/// }
143/// }
144///
145/// fn create_plugin<'a>(&'a self, host_info: HostInfo<'a>, plugin_id: &CStr) -> Option<PluginInstance<'a>> {
146/// if plugin_id == self.plugin_descriptor.id().unwrap_or_default() {
147/// Some(PluginInstance::new::<MyPlugin>(
148/// host_info,
149/// &self.plugin_descriptor,
150/// |_host| Ok(()) /* Create the shared struct */,
151/// |_host, _shared| Ok(()) /* Create the main thread struct */,
152/// ))
153/// } else {
154/// None
155/// }
156/// }
157/// }
158/// ```
159pub trait PluginFactoryImpl: Send + Sync {
160 /// Returns the number of plugins exposed by this factory.
161 fn plugin_count(&self) -> u32;
162
163 /// Returns the [`PluginDescriptor`] of the plugin that is assigned the given index.
164 ///
165 /// Hosts will usually call this method repeatedly with every index from 0 to the total returned
166 /// by [`plugin_count`](PluginFactoryImpl::plugin_count), in order to discover all the plugins
167 /// exposed by this factory.
168 ///
169 /// If the given index is out of bounds, or in general does not match any given plugin, this
170 /// returns [`None`].
171 fn plugin_descriptor(&self, index: u32) -> Option<&PluginDescriptor>;
172
173 /// Creates a new plugin instance for the plugin type matching the given `plugin_id`.
174 ///
175 /// If the given `plugin_id` matches against one of the plugin this factory manages,
176 /// implementors of this trait then use the [`PluginInstance::new`] method to instantiate the
177 /// corresponding plugin implementation.
178 ///
179 /// If the given `plugin_id` does not match any known plugins to this factory, this method
180 /// returns [`None`].
181 fn create_plugin<'a>(
182 &'a self,
183 host_info: HostInfo<'a>,
184 plugin_id: &CStr,
185 ) -> Option<PluginInstance<'a>>;
186}