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}