clap_clap/entry.rs
1/// Export `clap_entry` symbols and build a plugin factory.
2///
3/// Use this macro to build a CLAP plugin bundle from types that implement
4/// the [`Plugin`] trait.
5///
6/// [`Plugin`]: crate::plugin::Plugin
7///
8/// # Example
9///
10/// ```no_compile
11/// #[derive(Default)]
12/// struct MyPlugin;
13///
14/// impl Plugin for MyPlugin {
15/// ...
16/// }
17///
18/// #[derive(Default)]
19/// struct MyPluginToo;
20///
21/// impl Plugin for MyPluginToo {
22/// ...
23/// }
24///
25/// clap_clap::entry!(MyPlugin, MyPluginToo);
26/// ```
27///
28/// The crate that invokes the macro should be a dynamic library with C ABI.
29/// Specify the ABI in your crate's `Cargo.toml`:
30///
31/// ```toml
32/// [lib]
33/// crate-type = ["cdylib"]
34/// ```
35#[macro_export]
36macro_rules! entry {
37 ($($plug:ty),*) => {
38 pub mod _clap_entry {
39 use $crate::ffi::{
40 CLAP_PLUGIN_FACTORY_ID, CLAP_VERSION,
41 clap_plugin, clap_plugin_descriptor, clap_plugin_entry,
42 clap_plugin_factory, clap_host
43 };
44 use $crate::factory::{Factory, FactoryHost, FactoryPluginPrototype};
45 use $crate::plugin::Plugin;
46
47 use super::*; // Access the types supplied as macro arguments.
48
49 fn plugin_prototype<P: Plugin>() -> Box<FactoryPluginPrototype<P>> {
50 Box::new(FactoryPluginPrototype::build()
51 .expect("cannot build factory plugin descriptor"))
52 }
53
54 static FACTORY: std::sync::LazyLock<Factory> =
55 std::sync::LazyLock::new(||
56 Factory::new(vec![$(plugin_prototype::<$plug>(),)*])
57 );
58
59 /// SAFETY: CLAP requires this method to be thread-safe.
60 /// The LazyLock guarding FACTORY is thread-safe and
61 /// plugins_count() takes a shared reference to Factory.
62 unsafe extern "C-unwind" fn get_plugin_count(_: *const clap_plugin_factory) -> u32 {
63 FACTORY.plugins_count()
64 }
65
66 /// SAFETY: CLAP requires this method to be thread-safe.
67 /// The LazyLock guarding FACTORY is thread-safe and
68 /// descriptor() takes a shared reference to Factory.
69 unsafe extern "C-unwind" fn get_plugin_descriptor(
70 _: *const clap_plugin_factory,
71 index: u32,
72 ) -> *const clap_plugin_descriptor {
73 FACTORY.descriptor(index).unwrap_or(std::ptr::null())
74 }
75
76 /// SAFETY: CLAP requires this method to be thread-safe.
77 /// The LazyLock guarding FACTORY is thread-safe and
78 /// boxed_clap_plugin() takes a shared reference to Factory.
79 unsafe extern "C-unwind" fn create_plugin(
80 _: *const clap_plugin_factory,
81 host: *const clap_host,
82 plugin_id: *const std::ffi::c_char,
83 ) -> *const clap_plugin {
84 if plugin_id.is_null() {
85 return std::ptr::null();
86 }
87 // SAFETY: We checked if plug_id is non-null.
88 // The host guarantees that this is a valid C string now.
89 let plugin_id = unsafe { std::ffi::CStr::from_ptr(plugin_id) };
90
91 if host.is_null() {
92 return std::ptr::null();
93 }
94 let host = unsafe {&*host};
95 if host.get_extension.is_none()
96 || host.request_restart.is_none()
97 || host.request_process.is_none()
98 || host.request_callback.is_none() {
99 return std::ptr::null();
100 }
101 // SAFETY: We just checked that host and its methods are non-null.
102 let host = unsafe { FactoryHost::new_unchecked(host) };
103
104 FACTORY
105 .create_plugin(plugin_id, host)
106 .unwrap_or(std::ptr::null_mut())
107 }
108
109 static CLAP_PLUGIN_FACTORY: clap_plugin_factory = clap_plugin_factory {
110 get_plugin_count: Some(get_plugin_count),
111 get_plugin_descriptor: Some(get_plugin_descriptor),
112 create_plugin: Some(create_plugin),
113 };
114
115 unsafe extern "C-unwind" fn init(plugin_path: *const std::ffi::c_char) -> bool {
116 !plugin_path.is_null()
117 }
118
119 unsafe extern "C-unwind" fn deinit() {}
120
121 /// SAFETY: CLAP requires this method to be thread-safe.
122 unsafe extern "C-unwind" fn get_factory(factory_id: *const std::ffi::c_char) -> *const std::ffi::c_void {
123 if factory_id.is_null() {
124 return std::ptr::null();
125 }
126 // SAFETY: we cheched if factory_id is non-null.
127 // The host guarantees that this is a valid C string.
128 let id = unsafe { std::ffi::CStr::from_ptr(factory_id) };
129 if id == CLAP_PLUGIN_FACTORY_ID {
130 &raw const CLAP_PLUGIN_FACTORY as *const _
131 } else { std::ptr::null() }
132 }
133
134 #[allow(non_upper_case_globals)]
135 #[unsafe(no_mangle)]
136 #[used]
137 // Make this symbor public, so that plugin's own tests can access it.
138 pub static clap_entry: clap_plugin_entry = clap_plugin_entry {
139 clap_version: CLAP_VERSION,
140 init: Some(init),
141 deinit: Some(deinit),
142 get_factory: Some(get_factory),
143 };
144 }
145 };
146}