Skip to main content

clack_plugin/
entry.rs

1//! Types to expose and customize a CLAP file's entry.
2//!
3//! On Windows and Linux (and other non-macOS UNIXes), CLAP plugins are distributed as prebuilt
4//! dynamically-loaded libraries (usually `.dll` or `.so` files) with a `.clap` extension.
5//! They expose a single [`EntryDescriptor`], which, once initialized, acts as the entry
6//! point for the host to read into the library file.
7//!
8//! On macOS, CLAP plugins are distributed as a standard
9//! [bundle](https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html),
10//! which contains the aforementioned dynamically-loaded libraries as its executable file.
11//!
12//! A CLAP's [`Entry`] is the only exposed symbol in the library. Once
13//! [initialized](Entry::new), its role is to expose a number of [factories](Factory), which are
14//! singletons implementing various functionalities. The most relevant is the [`PluginFactory`](crate::factory::plugin::PluginFactoryImpl),
15//! which allows to list and instantiate plugins. See the [`factory`](crate::factory) module
16//! documentation to learn more about factories.
17//!
18//! An entry type can then be exposed to the host reading the library file by using the
19//! [`clack_export_entry`](crate::clack_export_entry) macro.
20//!
21//! See the [`Entry`] trait documentation for information and examples on how to implement your own
22//! entry type, or see the provided [`SinglePluginEntry`] convenience type if you only need to
23//! expose a single plugin type to the host.
24
25use crate::extensions::wrapper::handle_panic;
26use crate::factory::{Factory, FactoryImplementation};
27use std::error::Error;
28use std::ffi::{CStr, c_void};
29use std::fmt::{Display, Formatter};
30use std::hint::unreachable_unchecked;
31use std::panic::{AssertUnwindSafe, UnwindSafe};
32use std::ptr::NonNull;
33use std::sync::Mutex;
34
35pub use clack_common::entry::*;
36
37mod single;
38
39pub use single::{DefaultPluginFactory, SinglePluginEntry};
40
41/// A prelude that's helpful for implementing custom [`Entry`] and [`PluginFactory`](crate::factory::plugin::PluginFactoryImpl) types.
42pub mod prelude {
43    pub use crate::{
44        entry::{Entry, EntryDescriptor, EntryFactories, EntryLoadError, SinglePluginEntry},
45        factory::{
46            Factory,
47            plugin::{PluginFactoryImpl, PluginFactoryWrapper},
48        },
49        host::HostInfo,
50        plugin::{PluginDescriptor, PluginInstance},
51    };
52}
53
54/// A CLAP file's entry point.
55///
56/// This trait can be implemented by a custom, user-provided type in order to customize the library's
57/// entrypoint behavior. The [`clack_export_entry!`](crate::clack_export_entry) macro can then be
58/// used to set that type as the library's entry point to be discovered and loaded by hosts.
59///
60/// If you only care about the entry exposing a single plugin, you may use the [`SinglePluginEntry`]
61/// type instead of making your own.
62///
63/// To learn more about entries, refer to the [module documentation](self).
64///
65/// # Example
66///
67/// The following example shows how to create a custom entry and plugin factory that exposes
68/// two different plugins.
69///
70/// In this example, the second plugin also needs some custom initialization logic to create a
71/// (non-realtime-safe) channel between the `Shared` struct and the `MainThread` struct. This is
72/// also not possible to do with [`SinglePluginEntry`], since only allows for pure functions.
73///
74/// ```
75/// use std::ffi::CStr;
76/// use std::sync::mpsc::{channel, Receiver, Sender};
77/// use clack_plugin::entry::prelude::*;
78/// use clack_plugin::prelude::*;
79///
80/// pub struct MyFirstPlugin;
81/// pub struct MySecondPlugin;
82///
83/// impl Plugin for MyFirstPlugin {
84///     type AudioProcessor<'a> = ();
85///     type Shared<'a> = ();
86///     type MainThread<'a> = ();
87/// }
88///
89/// impl Plugin for MySecondPlugin {
90///     type AudioProcessor<'a> = ();
91///     type Shared<'a> = MySecondPluginShared;
92///     type MainThread<'a> = MySecondPluginMainThread;
93/// }
94///
95/// // For the sake of this example, this plugin just needs to share Strings around.
96/// pub struct MySecondPluginShared(Sender<String>);
97/// pub struct MySecondPluginMainThread(Receiver<String>);
98///
99/// impl PluginShared<'_> for MySecondPluginShared {}
100/// impl PluginMainThread<'_, MySecondPluginShared> for MySecondPluginMainThread {}
101///
102/// // Our entry implementation
103///
104/// pub struct MyEntry {
105///     plugin_factory: PluginFactoryWrapper<MyPluginFactory>
106/// }
107///
108/// impl Entry for MyEntry {
109///     fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError> {
110///         // Initialize the factory and its wrapper
111///         Ok(Self { plugin_factory: PluginFactoryWrapper::new(MyPluginFactory::new()) })
112///     }
113///
114///     fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>) {
115///         // Expose MyPluginFactory as an available factory, using the wrapper
116///         builder.register_factory(&self.plugin_factory);
117///     }
118/// }
119///
120/// // Our factory holds the descriptors for both of our plugins
121/// pub struct MyPluginFactory {
122///     first_plugin: PluginDescriptor,
123///     second_plugin: PluginDescriptor,
124/// }
125///
126/// impl MyPluginFactory {
127///     pub fn new() -> Self {
128///         Self {
129///             first_plugin: PluginDescriptor::new("my.plugin.first", "My first plugin"),
130///             second_plugin: PluginDescriptor::new("my.plugin.second", "My second plugin"),
131///         }
132///     }
133/// }
134///
135/// impl PluginFactoryImpl for MyPluginFactory {
136///     fn plugin_count(&self) -> u32 {
137///         2 // We have 2 plugins to expose to the host
138///     }
139///
140///     // Gets the plugin descriptor matching the given index.
141///     // It doesn't matter much which plugin has which index,
142///     // but each of our plugins must have a unique one.
143///     fn plugin_descriptor(&self, index: u32) -> Option<&PluginDescriptor> {
144///         match index {
145///             0 => Some(&self.first_plugin),
146///             1 => Some(&self.second_plugin),
147///             _ => None,
148///         }
149///     }
150///
151///     // Called when the host desires to create a new instance of one of our plugins.
152///     // Which plugin it is, is determined by the given plugin_id.
153///     fn create_plugin<'a>(
154///         &'a self,
155///         host_info: HostInfo<'a>,
156///         plugin_id: &CStr,
157///     ) -> Option<PluginInstance<'a>> {
158///         if plugin_id == self.first_plugin.id().unwrap_or_default() {
159///             // We can use the PluginInstance type to easily make a new instance of a given
160///             // plugin type.
161///             Some(PluginInstance::new::<MyFirstPlugin>(
162///                 host_info,
163///                 &self.first_plugin,
164///                 |_host| Ok(()) /* Create the shared struct */,
165///                 |_host, _shared| Ok(()) /* Create the main thread struct */,
166///             ))
167///         } else if plugin_id == self.second_plugin.id().unwrap_or_default() {
168///             let (tx, rx) = channel();
169///
170///             Some(PluginInstance::new::<MySecondPlugin>(
171///                 host_info,
172///                 &self.second_plugin,
173///                 |_host| Ok(MySecondPluginShared(tx)) /* Create the shared struct */,
174///                 |_host, _shared| Ok(MySecondPluginMainThread(rx)) /* Create the main thread struct */,
175///             ))
176///         } else {
177///             None
178///         }
179///     }
180/// }
181///
182/// clack_export_entry!(MyEntry);
183/// ```
184pub trait Entry: Sized + Send + Sync + 'static {
185    /// Instantiates the entry.
186    ///
187    /// The path of the bundle file this entry was loaded from is also given by the host, in case
188    /// extra neighboring files need to be loaded.
189    ///
190    /// # Host Compatibility Notes
191    ///
192    /// Some hosts might misbehave and pass an empty path to this function, or might even forget
193    /// to properly initialize the entry before trying to get factories from it.
194    ///
195    /// In those cases, Clack will call this function to instantiate the entry "just-in-time", but
196    /// `None` will be passed to this function instead of the path.
197    ///
198    /// This is still invalid host behavior according to the CLAP specification. Therefore, it is
199    /// completely valid for implementations of this function to always return [`EntryLoadError`]
200    /// when `None` is passed instead of a path.
201    ///
202    /// However, this allows plugins that may not need the bundle file path to initialize, and at
203    /// least partially recover.
204    ///
205    /// # Platform Compatibility notes
206    ///
207    /// On Windows and Linux, this `bundle_path` is the one of the dynamic library file this entry
208    /// was loaded from.
209    ///
210    /// On macOS, `bundle_path` should be the path of the actual bundle directory the library
211    /// this entry was loaded from, resides in.
212    ///
213    /// # Errors
214    ///
215    /// This returns [`EntryLoadError`] if any error occurred during instantiation.
216    fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError>;
217
218    /// Declares the factories this entry exposes to the host, by registering them to the given
219    /// [`EntryFactories`] builder.
220    ///
221    /// See the [`EntryFactories`] documentation for more information.
222    fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>);
223}
224
225/// An error indicating an entry has failed loading.
226///
227/// This error is returned by [`Entry::new`] when it fails.
228#[derive(Copy, Clone, Debug)]
229pub struct EntryLoadError;
230
231impl Display for EntryLoadError {
232    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233        f.write_str("Entry failed to load.")
234    }
235}
236
237impl Error for EntryLoadError {}
238
239/// Exposes a given [`Entry`] type to the host as *the* library's entry point.
240///
241/// This macro exports the standard CLAP symbol `clap_entry`, set to an [`EntryDescriptor`] which
242/// relies on the given `entry_type` for behavior.
243///
244/// Note this means you cannot call this macro twice in the same executable, as the produced symbols
245/// will conflict.
246///
247/// # Example
248///
249/// This example exposes a custom `MyEntry` entry type. See the [`Entry`] trait documentation for
250/// an example of how to actually implement it.
251///
252/// ```
253/// use std::ffi::CStr;
254/// use clack_plugin::entry::prelude::*;
255/// use clack_plugin::prelude::*;
256///
257/// pub struct MyEntry;
258///
259/// impl Entry for MyEntry {
260/// #    fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError> {
261/// #        unreachable!()
262/// #    }
263///     /* ... */
264/// #    fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>) {
265/// #        unreachable!()
266/// #    }
267/// }
268///
269/// // The host will now see and use this entry.
270/// clack_export_entry!(MyEntry);
271/// ```
272#[macro_export]
273macro_rules! clack_export_entry {
274    ($entry_type:ty, $entry_lambda:expr) => {
275        #[allow(non_upper_case_globals, missing_docs)]
276        #[allow(unsafe_code)]
277        #[allow(warnings, unused)]
278        #[unsafe(no_mangle)]
279        pub static clap_entry: $crate::entry::EntryDescriptor =
280            $crate::clack_entry!($entry_type, $entry_lambda);
281    };
282    ($entry_type:ty) => {
283        #[allow(non_upper_case_globals, missing_docs)]
284        #[allow(unsafe_code)]
285        #[allow(warnings, unused)]
286        #[unsafe(no_mangle)]
287        pub static clap_entry: $crate::entry::EntryDescriptor = $crate::clack_entry!($entry_type);
288    };
289}
290
291/// Produces an [`EntryDescriptor`] value from a given [`Entry`] type, but without exposing it.
292///
293/// This can be useful as an alternative to the usual
294/// [`clack_export_entry`](crate::clack_export_entry) macro if you do not want or need to export the
295/// given entry, and just need an [`EntryDescriptor`].
296#[macro_export]
297macro_rules! clack_entry {
298    ($entry_type:ty, $entry_lambda:expr) => {
299        ({
300            #[allow(unsafe_code)]
301            const fn _entry() -> $crate::entry::EntryDescriptor {
302                static HOLDER: $crate::entry::EntryHolder<$entry_type> =
303                    $crate::entry::EntryHolder::new();
304
305                unsafe extern "C" fn init(plugin_path: *const ::core::ffi::c_char) -> bool {
306                    HOLDER.init_with(plugin_path, $entry_lambda)
307                }
308
309                unsafe extern "C" fn deinit() {
310                    HOLDER.de_init()
311                }
312
313                unsafe extern "C" fn get_factory(
314                    identifier: *const ::core::ffi::c_char,
315                ) -> *const ::core::ffi::c_void {
316                    HOLDER.get_factory_with(identifier, $entry_lambda)
317                }
318
319                $crate::entry::EntryDescriptor {
320                    clap_version: $crate::utils::ClapVersion::CURRENT.to_raw(),
321                    init: Some(init),
322                    deinit: Some(deinit),
323                    get_factory: Some(get_factory),
324                }
325            }
326            _entry()
327        })
328    };
329    ($entry_type:ty) => {
330        ({
331            #[allow(unsafe_code)]
332            const fn _entry() -> $crate::entry::EntryDescriptor {
333                static HOLDER: $crate::entry::EntryHolder<$entry_type> =
334                    $crate::entry::EntryHolder::new();
335
336                unsafe extern "C" fn init(plugin_path: *const ::core::ffi::c_char) -> bool {
337                    HOLDER.init(plugin_path)
338                }
339
340                unsafe extern "C" fn deinit() {
341                    HOLDER.de_init()
342                }
343
344                unsafe extern "C" fn get_factory(
345                    identifier: *const ::core::ffi::c_char,
346                ) -> *const ::core::ffi::c_void {
347                    HOLDER.get_factory(identifier)
348                }
349
350                $crate::entry::EntryDescriptor {
351                    clap_version: $crate::utils::ClapVersion::CURRENT.to_raw(),
352                    init: Some(init),
353                    deinit: Some(deinit),
354                    get_factory: Some(get_factory),
355                }
356            }
357            _entry()
358        })
359    };
360}
361
362/// A lightweight collection of all the factories a given plugin entry supports.
363///
364/// [`Entry`] implementations are expected to declare the factories they support to the host using
365/// the [`register_factory`](EntryFactories::register_factory) method.
366pub struct EntryFactories<'a> {
367    found: Option<NonNull<c_void>>,
368    requested: &'a [&'a CStr],
369}
370
371impl<'a> EntryFactories<'a> {
372    #[doc(hidden)]
373    #[inline]
374    pub fn new(requested: &'a [&'a CStr]) -> Self {
375        Self {
376            found: None,
377            requested,
378        }
379    }
380
381    #[doc(hidden)]
382    #[inline]
383    pub fn found(&self) -> Option<NonNull<c_void>> {
384        self.found
385    }
386
387    /// Adds a given factory implementation to the list of factories this entry supports.
388    ///
389    /// This method returns the factory itself, allowing for easy method chaining and a builder-like
390    /// usage pattern.
391    ///
392    /// See [`Entry`]'s documentation for an example.
393    pub fn register_factory<F: FactoryImplementation<'a>>(&mut self, factory: &'a F) -> &mut Self {
394        if self.found.is_some() {
395            return self;
396        }
397
398        for requested in self.requested {
399            if F::Factory::IDENTIFIERS.contains(requested) {
400                self.found = Some(factory.wrapper().as_raw().as_raw().cast());
401                break;
402            }
403        }
404
405        self
406    }
407}
408
409enum EntryHolderInner<E> {
410    Initialized { reference_count: usize, entry: E },
411    Uninitialized,
412}
413
414#[doc(hidden)]
415pub struct EntryHolder<E: Entry> {
416    inner: Mutex<EntryHolderInner<E>>,
417}
418
419use crate::entry::EntryHolderInner::*;
420
421#[doc(hidden)]
422impl<E: Entry> EntryHolder<E> {
423    #[allow(clippy::new_without_default)] // This is actually a private type
424    pub const fn new() -> Self {
425        Self {
426            inner: Mutex::new(Uninitialized),
427        }
428    }
429
430    /// # Safety
431    ///
432    /// Users *must* ensure this is called at least once before any other method, and that
433    /// `plugin_path` points to a valid, NULL-terminated C string.
434    #[inline]
435    pub unsafe fn init(&self, plugin_path: *const core::ffi::c_char) -> bool {
436        self.init_with(plugin_path, |p| E::new(p))
437    }
438
439    /// # Safety
440    ///
441    /// Users *must* ensure this is called at least once before any other method, and that
442    /// `plugin_path` points to a valid, NULL-terminated C string.
443    pub unsafe fn init_with(
444        &self,
445        plugin_path: *const core::ffi::c_char,
446        entry_factory: impl FnOnce(Option<&CStr>) -> Result<E, EntryLoadError> + UnwindSafe,
447    ) -> bool {
448        let Ok(Ok(mut inner)) = handle_panic(|| self.inner.lock()) else {
449            // A poisoned lock means init() panicked, so we consider the entry unusable.
450            // Same if lock() itself panicked.
451            return false;
452        };
453
454        match &mut *inner {
455            Initialized {
456                reference_count, ..
457            } => {
458                *reference_count += 1;
459                true
460            }
461            Uninitialized => {
462                let plugin_path = CStr::from_ptr(plugin_path);
463                let plugin_path = (!plugin_path.is_empty()).then_some(plugin_path);
464                let entry = handle_panic(|| entry_factory(plugin_path));
465
466                if let Ok(Ok(entry)) = entry {
467                    *inner = Initialized {
468                        entry,
469                        reference_count: 1,
470                    };
471                    true
472                } else {
473                    false
474                }
475            }
476        }
477    }
478
479    /// # Safety
480    ///
481    /// Users must *only* call this after they are done with the whole entry.
482    pub unsafe fn de_init(&self) {
483        let Ok(mut inner) = self.inner.lock() else {
484            return;
485        };
486
487        if let Initialized {
488            reference_count, ..
489        } = &mut *inner
490        {
491            if *reference_count > 1 {
492                *reference_count -= 1;
493            } else {
494                let _ = handle_panic(AssertUnwindSafe(|| *inner = Uninitialized));
495            }
496        }
497    }
498
499    /// # Safety
500    ///
501    /// Users *must* ensure this is called at least once before any other method, and that
502    /// `plugin_path` points to a valid, NULL-terminated C string.
503    #[inline]
504    pub unsafe fn get_factory(&self, identifier: *const core::ffi::c_char) -> *const c_void {
505        self.get_factory_with(identifier, |p| E::new(p))
506    }
507
508    /// # Safety
509    ///
510    /// This must only be called between calls to init and de_init, and identifier must point to a
511    /// valid, NULL-terminated C string.
512    pub unsafe fn get_factory_with(
513        &self,
514        identifier: *const core::ffi::c_char,
515        entry_factory: impl FnOnce(Option<&CStr>) -> Result<E, EntryLoadError> + UnwindSafe,
516    ) -> *const c_void {
517        if identifier.is_null() {
518            return core::ptr::null();
519        }
520
521        let Ok(mut inner) = self.inner.lock() else {
522            return core::ptr::null();
523        };
524
525        let entry = match &*inner {
526            Initialized { entry, .. } => entry,
527            Uninitialized => {
528                let entry = handle_panic(|| entry_factory(None));
529
530                if let Ok(Ok(entry)) = entry {
531                    *inner = Initialized {
532                        entry,
533                        // This will leak the entry if deinit() is not called afterward, but it's better than
534                        // straight-up failing to load.
535                        reference_count: 1,
536                    };
537
538                    let Initialized { entry, .. } = &*inner else {
539                        // SAFETY: we just initialized it to Initialized above
540                        unsafe {
541                            unreachable_unchecked();
542                        }
543                    };
544
545                    entry
546                } else {
547                    return core::ptr::null();
548                }
549            }
550        };
551
552        let identifier = CStr::from_ptr(identifier);
553
554        let result = handle_panic(AssertUnwindSafe(|| {
555            let identifiers = &[identifier];
556            let mut builder = EntryFactories::new(identifiers);
557            entry.declare_factories(&mut builder);
558            builder.found()
559        }));
560
561        match result {
562            Ok(Some(found)) => found.as_ptr(),
563            _ => core::ptr::null(),
564        }
565    }
566}