diff --git a/src/wrapper/clap/factory.rs b/src/wrapper/clap/factory.rs index 85f75db6..d5997300 100644 --- a/src/wrapper/clap/factory.rs +++ b/src/wrapper/clap/factory.rs @@ -1,8 +1,12 @@ use clap_sys::host::clap_host; use clap_sys::plugin::{clap_plugin, clap_plugin_descriptor}; use clap_sys::plugin_factory::clap_plugin_factory; +use clap_sys::version::CLAP_VERSION; +use std::ffi::CString; use std::marker::PhantomData; +use std::mem::MaybeUninit; use std::os::raw::c_char; +use std::ptr; use crate::ClapPlugin; @@ -14,23 +18,92 @@ pub struct Factory { // Keep the vtable as the first field so we can do a simple pointer cast pub clap_plugin_factory: clap_plugin_factory, + // We need [CString]s for all of `ClapPlugin`'s `&str` fields + clap_id: CString, + name: CString, + vendor: CString, + url: CString, + clap_manual_url: CString, + clap_support_url: CString, + version: CString, + clap_description: CString, + clap_features: Vec, + clap_features_ptrs: MaybeUninit, + + /// We only support a single plugin per factory right now, so we'll fill in the plugin + /// descriptor upfront. We also need to initialize the `CString` fields above first before we + /// can initialize this plugin descriptor. + plugin_descriptor: MaybeUninit, + /// The type will be used for constructing plugin instances later. _phantom: PhantomData

, } +/// Needed for the Send+Sync implementation for lazy_static. +struct CStrPtrs(Vec<*const c_char>); + impl Default for Factory

{ fn default() -> Self { - Self { + let mut factory = Self { clap_plugin_factory: clap_plugin_factory { get_plugin_count: Self::get_plugin_count, get_plugin_descriptor: Self::get_plugin_descriptor, create_plugin: Self::create_plugin, }, + clap_id: CString::new(P::CLAP_ID).expect("`CLAP_ID` contained null bytes"), + name: CString::new(P::NAME).expect("`NAME` contained null bytes"), + vendor: CString::new(P::VENDOR).expect("`VENDOR` contained null bytes"), + url: CString::new(P::URL).expect("`URL` contained null bytes"), + clap_manual_url: CString::new(P::CLAP_MANUAL_URL) + .expect("`CLAP_MANUAL_URL` contained null bytes"), + clap_support_url: CString::new(P::CLAP_SUPPORT_URL) + .expect("`CLAP_SUPPORT_URL` contained null bytes"), + version: CString::new(P::VERSION).expect("`VERSION` contained null bytes"), + clap_description: CString::new(P::CLAP_DESCRIPTION) + .expect("`CLAP_DESCRIPTION` contained null bytes"), + clap_features: P::CLAP_FEATURES + .iter() + .map(|s| CString::new(*s).expect("`CLAP_FEATURES` contained null bytes")) + .collect(), + clap_features_ptrs: MaybeUninit::uninit(), + plugin_descriptor: MaybeUninit::uninit(), _phantom: PhantomData, - } + }; + + // The keyword list is an environ-like list of char pointers terminated by a null pointer + let mut clap_features_ptrs: Vec<*const c_char> = factory + .clap_features + .iter() + .map(|feature| feature.as_ptr()) + .collect(); + clap_features_ptrs.push(ptr::null()); + factory + .clap_features_ptrs + .write(CStrPtrs(clap_features_ptrs)); + + // We couldn't initialize this directly because of all the CStrings + factory.plugin_descriptor.write(clap_plugin_descriptor { + clap_version: CLAP_VERSION, + id: factory.clap_id.as_ptr(), + name: factory.name.as_ptr(), + vendor: factory.vendor.as_ptr(), + url: factory.url.as_ptr(), + manual_url: factory.clap_manual_url.as_ptr(), + support_url: factory.clap_support_url.as_ptr(), + version: factory.version.as_ptr(), + description: factory.clap_description.as_ptr(), + features: unsafe { factory.clap_features_ptrs.assume_init_ref() } + .0 + .as_ptr(), + }); + + factory } } +unsafe impl Send for CStrPtrs {} +unsafe impl Sync for CStrPtrs {} + impl Factory

{ unsafe extern "C" fn get_plugin_count(_factory: *const clap_plugin_factory) -> u32 { 1 @@ -40,7 +113,13 @@ impl Factory

{ factory: *const clap_plugin_factory, index: u32, ) -> *const clap_plugin_descriptor { - todo!() + let factory = &*(factory as *const Self); + + if index == 0 { + factory.plugin_descriptor.assume_init_ref() + } else { + ptr::null() + } } unsafe extern "C" fn create_plugin(