pub trait Entry:
Sized
+ Send
+ Sync
+ 'static {
// Required methods
fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError>;
fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>);
}Expand description
A CLAP file’s entry point.
This trait can be implemented by a custom, user-provided type in order to customize the library’s
entrypoint behavior. The clack_export_entry! macro can then be
used to set that type as the library’s entry point to be discovered and loaded by hosts.
If you only care about the entry exposing a single plugin, you may use the SinglePluginEntry
type instead of making your own.
To learn more about entries, refer to the module documentation.
§Example
The following example shows how to create a custom entry and plugin factory that exposes two different plugins.
In this example, the second plugin also needs some custom initialization logic to create a
(non-realtime-safe) channel between the Shared struct and the MainThread struct. This is
also not possible to do with SinglePluginEntry, since only allows for pure functions.
use std::ffi::CStr;
use std::sync::mpsc::{channel, Receiver, Sender};
use clack_plugin::entry::prelude::*;
use clack_plugin::prelude::*;
pub struct MyFirstPlugin;
pub struct MySecondPlugin;
impl Plugin for MyFirstPlugin {
type AudioProcessor<'a> = ();
type Shared<'a> = ();
type MainThread<'a> = ();
}
impl Plugin for MySecondPlugin {
type AudioProcessor<'a> = ();
type Shared<'a> = MySecondPluginShared;
type MainThread<'a> = MySecondPluginMainThread;
}
// For the sake of this example, this plugin just needs to share Strings around.
pub struct MySecondPluginShared(Sender<String>);
pub struct MySecondPluginMainThread(Receiver<String>);
impl PluginShared<'_> for MySecondPluginShared {}
impl PluginMainThread<'_, MySecondPluginShared> for MySecondPluginMainThread {}
// Our entry implementation
pub struct MyEntry {
plugin_factory: PluginFactoryWrapper<MyPluginFactory>
}
impl Entry for MyEntry {
fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError> {
// Initialize the factory and its wrapper
Ok(Self { plugin_factory: PluginFactoryWrapper::new(MyPluginFactory::new()) })
}
fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>) {
// Expose MyPluginFactory as an available factory, using the wrapper
builder.register_factory(&self.plugin_factory);
}
}
// Our factory holds the descriptors for both of our plugins
pub struct MyPluginFactory {
first_plugin: PluginDescriptor,
second_plugin: PluginDescriptor,
}
impl MyPluginFactory {
pub fn new() -> Self {
Self {
first_plugin: PluginDescriptor::new("my.plugin.first", "My first plugin"),
second_plugin: PluginDescriptor::new("my.plugin.second", "My second plugin"),
}
}
}
impl PluginFactoryImpl for MyPluginFactory {
fn plugin_count(&self) -> u32 {
2 // We have 2 plugins to expose to the host
}
// Gets the plugin descriptor matching the given index.
// It doesn't matter much which plugin has which index,
// but each of our plugins must have a unique one.
fn plugin_descriptor(&self, index: u32) -> Option<&PluginDescriptor> {
match index {
0 => Some(&self.first_plugin),
1 => Some(&self.second_plugin),
_ => None,
}
}
// Called when the host desires to create a new instance of one of our plugins.
// Which plugin it is, is determined by the given plugin_id.
fn create_plugin<'a>(
&'a self,
host_info: HostInfo<'a>,
plugin_id: &CStr,
) -> Option<PluginInstance<'a>> {
if plugin_id == self.first_plugin.id().unwrap_or_default() {
// We can use the PluginInstance type to easily make a new instance of a given
// plugin type.
Some(PluginInstance::new::<MyFirstPlugin>(
host_info,
&self.first_plugin,
|_host| Ok(()) /* Create the shared struct */,
|_host, _shared| Ok(()) /* Create the main thread struct */,
))
} else if plugin_id == self.second_plugin.id().unwrap_or_default() {
let (tx, rx) = channel();
Some(PluginInstance::new::<MySecondPlugin>(
host_info,
&self.second_plugin,
|_host| Ok(MySecondPluginShared(tx)) /* Create the shared struct */,
|_host, _shared| Ok(MySecondPluginMainThread(rx)) /* Create the main thread struct */,
))
} else {
None
}
}
}
clack_export_entry!(MyEntry);Required Methods§
Sourcefn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError>
fn new(bundle_path: Option<&CStr>) -> Result<Self, EntryLoadError>
Instantiates the entry.
The path of the bundle file this entry was loaded from is also given by the host, in case extra neighboring files need to be loaded.
§Host Compatibility Notes
Some hosts might misbehave and pass an empty path to this function, or might even forget to properly initialize the entry before trying to get factories from it.
In those cases, Clack will call this function to instantiate the entry “just-in-time”, but
None will be passed to this function instead of the path.
This is still invalid host behavior according to the CLAP specification. Therefore, it is
completely valid for implementations of this function to always return EntryLoadError
when None is passed instead of a path.
However, this allows plugins that may not need the bundle file path to initialize, and at least partially recover.
§Platform Compatibility notes
On Windows and Linux, this bundle_path is the one of the dynamic library file this entry
was loaded from.
On macOS, bundle_path should be the path of the actual bundle directory the library
this entry was loaded from, resides in.
§Errors
This returns EntryLoadError if any error occurred during instantiation.
Sourcefn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>)
fn declare_factories<'a>(&'a self, builder: &mut EntryFactories<'a>)
Declares the factories this entry exposes to the host, by registering them to the given
EntryFactories builder.
See the EntryFactories documentation for more information.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".