Skip to main content

Entry

Trait Entry 

Source
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§

Source

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.

Source

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".

Implementors§