use crate::plugin::DestroyLock;
use crate::prelude::*;
use crate::util::UnsafeOptionCell;
use clap_sys::ext::log::{
CLAP_LOG_ERROR, CLAP_LOG_HOST_MISBEHAVING, CLAP_LOG_PLUGIN_MISBEHAVING, clap_log_severity,
};
use clap_sys::host::clap_host;
use clap_sys::plugin::clap_plugin;
use std::borrow::Cow;
use std::cell::OnceCell;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt::{Display, Formatter};
use std::io::Write;
use std::panic::AssertUnwindSafe;
use std::pin::Pin;
use std::ptr::NonNull;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Once, OnceLock};
#[cfg(not(test))]
#[allow(unused)]
use std::panic::catch_unwind as handle_panic;
#[cfg(test)]
#[inline]
#[allow(unused)]
fn handle_panic<F: FnOnce() -> R, R>(f: F) -> std::thread::Result<R> {
Ok(f())
}
pub(crate) mod descriptor;
mod logging;
pub struct HostWrapper<H: HostHandlers> {
audio_processor: UnsafeOptionCell<<H as HostHandlers>::AudioProcessor<'static>>,
main_thread: OnceCell<<H as HostHandlers>::MainThread<'static>>,
shared: <H as HostHandlers>::Shared<'static>,
init_guard: Once,
init_started: AtomicBool,
plugin_ptr: OnceLock<NonNull<clap_plugin>>,
destroy_lock: Arc<DestroyLock>,
}
unsafe impl<H: HostHandlers> Send for HostWrapper<H> {}
unsafe impl<H: HostHandlers> Sync for HostWrapper<H> {}
impl<H: HostHandlers> HostWrapper<H> {
pub unsafe fn handle<T, F>(host: *const clap_host, handler: F) -> Option<T>
where
F: FnOnce(&HostWrapper<H>) -> Result<T, HostWrapperError>,
{
let result = Self::from_raw(host).and_then(|h| {
Self::handle_panic(h, |h| {
h.ensure_initializing_called();
handler(h)
})
});
match result {
Ok(value) => Some(value),
Err(e) => {
logging::host_log(host, &e);
None
}
}
}
pub unsafe fn handle_main_thread<T, F>(host: *const clap_host, handler: F) -> Option<T>
where
F: for<'a> FnOnce(&<H as HostHandlers>::MainThread<'a>) -> Result<T, HostWrapperError>,
{
Self::handle(host, |host| host.on_main_thread(handler).transpose()).flatten()
}
pub(crate) unsafe fn on_main_thread<T>(
&self,
handler: impl for<'a> FnOnce(&<H as HostHandlers>::MainThread<'a>) -> T,
) -> Option<T> {
Some(handler(self.main_thread.get().as_ref()?))
}
#[inline]
pub unsafe fn audio_processor(
&self,
) -> Result<NonNull<<H as HostHandlers>::AudioProcessor<'_>>, HostWrapperError> {
let ptr = self
.audio_processor
.as_ptr()
.ok_or(HostWrapperError::DeactivatedPlugin)?;
Ok(ptr.cast())
}
#[inline]
pub fn shared(&self) -> &<H as HostHandlers>::Shared<'_> {
unsafe { shrink_shared_ref::<H>(&self.shared) }
}
pub(crate) fn new<FS, FH>(shared: FS, main_thread: FH) -> Pin<Arc<Self>>
where
FS: for<'s> FnOnce(&'s ()) -> <H as HostHandlers>::Shared<'s>,
FH: for<'s> FnOnce(
&'s <H as HostHandlers>::Shared<'s>,
) -> <H as HostHandlers>::MainThread<'s>,
{
let wrapper = Arc::new(Self {
audio_processor: UnsafeOptionCell::new(),
main_thread: OnceCell::new(),
shared: shared(&()),
init_guard: Once::new(),
init_started: AtomicBool::new(false),
plugin_ptr: OnceLock::new(),
destroy_lock: Arc::new(DestroyLock::new()),
});
let main_thread = main_thread(unsafe { extend_shared_ref(&wrapper.shared) });
let Ok(()) = wrapper.main_thread.set(main_thread) else {
unreachable!()
};
unsafe { Pin::new_unchecked(wrapper) }
}
pub(crate) unsafe fn created(&self, instance: NonNull<clap_plugin>) {
let _ = self.plugin_ptr.set(instance);
}
pub(crate) unsafe fn instantiated(&self) {
self.ensure_initializing_called();
let instance = *self.plugin_ptr.get().unwrap();
self.main_thread
.get()
.unwrap()
.initialized(InitializedPluginHandle::new(
self.destroy_lock.clone(),
instance,
));
}
pub(crate) fn start_instance_destroy(&self) {
self.destroy_lock.start_destroying();
}
#[inline]
pub(crate) unsafe fn setup_audio_processor<FA>(
&self,
audio_processor: FA,
) -> Result<(), PluginInstanceError>
where
FA: for<'a> FnOnce(
&'a <H as HostHandlers>::Shared<'a>,
&<H as HostHandlers>::MainThread<'a>,
) -> <H as HostHandlers>::AudioProcessor<'a>,
{
if self.audio_processor.is_some() {
return Err(PluginInstanceError::AlreadyActivatedPlugin);
}
self.audio_processor.put(audio_processor(
unsafe { extend_shared_ref(&self.shared) },
self.main_thread.get().unwrap(),
));
Ok(())
}
#[inline]
pub(crate) unsafe fn teardown_audio_processor<T>(
&self,
drop: impl for<'s> FnOnce(
<H as HostHandlers>::AudioProcessor<'s>,
&<H as HostHandlers>::MainThread<'s>,
) -> T,
) -> Result<T, PluginInstanceError> {
match self.audio_processor.take() {
None => Err(PluginInstanceError::DeactivatedPlugin),
Some(audio_processor) => Ok(drop(
audio_processor,
self.main_thread.get().unwrap(),
)),
}
}
#[inline]
pub(crate) fn is_active(&self) -> bool {
self.audio_processor.is_some()
}
pub(crate) fn handle_panic<T, F, Pa>(param: Pa, handler: F) -> Result<T, HostWrapperError>
where
F: FnOnce(Pa) -> Result<T, HostWrapperError>,
{
handle_panic(AssertUnwindSafe(|| handler(param))).map_err(|_| HostWrapperError::Panic)?
}
unsafe fn from_raw<'a>(raw: *const clap_host) -> Result<&'a Self, HostWrapperError> {
raw.as_ref()
.ok_or(HostWrapperError::NullHostInstance)?
.host_data
.cast::<HostWrapper<H>>()
.as_ref()
.ok_or(HostWrapperError::NullHostData)
}
fn ensure_initializing_called(&self) {
let Some(ptr) = self.plugin_ptr.get().copied() else {
return;
};
self.init_guard.call_once_force(|_| {
let result =
self.init_started
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
if result == Ok(false) {
let handle =
unsafe { InitializingPluginHandle::new(self.destroy_lock.clone(), ptr) };
self.shared().initializing(handle);
}
})
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum HostWrapperError {
InvalidParameter(&'static str),
NullHostInstance,
NullHostData,
Panic,
DeactivatedPlugin,
ErrorFormatError(std::io::Error),
Error(Box<dyn Error + 'static>),
Message(&'static str),
}
impl HostWrapperError {
fn severity(&self) -> clap_log_severity {
match self {
HostWrapperError::NullHostInstance => CLAP_LOG_PLUGIN_MISBEHAVING,
HostWrapperError::InvalidParameter(_) => CLAP_LOG_PLUGIN_MISBEHAVING,
HostWrapperError::NullHostData => CLAP_LOG_HOST_MISBEHAVING,
HostWrapperError::Panic => CLAP_LOG_HOST_MISBEHAVING,
HostWrapperError::DeactivatedPlugin => CLAP_LOG_PLUGIN_MISBEHAVING,
HostWrapperError::ErrorFormatError(_) => CLAP_LOG_HOST_MISBEHAVING,
_ => CLAP_LOG_ERROR,
}
}
pub fn format_cstr(&self) -> Cow<'static, CStr> {
let mut buf = Vec::new();
match buf.write_fmt(format_args!("{}", self)) {
Ok(()) => {}
Err(e) => {
if matches!(self, HostWrapperError::ErrorFormatError(_)) {
return c"Failed to format error".into();
}
return HostWrapperError::ErrorFormatError(e).format_cstr();
}
};
if let Some(idx) = buf.iter().position(|&b| b == 0) {
buf.truncate(idx);
}
let Ok(str) = CString::new(buf) else {
return c"Error format failure: NulError".into();
};
str.into()
}
}
impl Display for HostWrapperError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
use HostWrapperError::*;
match self {
NullHostInstance => f.write_str("Host instance pointer is NULL"),
NullHostData => f.write_str("Host data pointer is NULL"),
InvalidParameter(s) => write!(f, "Invalid parameter: {s}"),
Panic => f.write_str("Host callback panicked"),
DeactivatedPlugin => {
f.write_str("Tried to call an audio-thread method while the plugin was deactivated")
}
Message(s) => f.write_str(s),
Error(e) => Display::fmt(e, f),
ErrorFormatError(e) => {
write!(f, "Error while formatting host error: '{e}'")
}
}
}
}
impl From<HostError> for HostWrapperError {
#[inline]
fn from(value: HostError) -> Self {
match value {
HostError::Error(e) => Self::Error(e),
HostError::Message(e) => Self::Message(e),
}
}
}
unsafe fn extend_shared_ref<'a, H: SharedHandler<'a>>(shared: &H) -> &'a H {
&*(shared as *const _)
}
unsafe fn shrink_shared_ref<'a, 'instance, H: HostHandlers>(
shared: &'a H::Shared<'instance>,
) -> &'a H::Shared<'a> {
let original_ptr = shared as *const H::Shared<'instance>;
let transmuted_ptr: *const H::Shared<'a> = original_ptr.cast();
&*transmuted_ptr
}