1use crate::plugin::DestroyLock;
4use crate::prelude::*;
5use crate::util::UnsafeOptionCell;
6use clap_sys::ext::log::{
7 CLAP_LOG_ERROR, CLAP_LOG_HOST_MISBEHAVING, CLAP_LOG_PLUGIN_MISBEHAVING, clap_log_severity,
8};
9use clap_sys::host::clap_host;
10use clap_sys::plugin::clap_plugin;
11use std::borrow::Cow;
12use std::cell::OnceCell;
13use std::error::Error;
14use std::ffi::{CStr, CString};
15use std::fmt::{Display, Formatter};
16use std::io::Write;
17use std::panic::AssertUnwindSafe;
18use std::pin::Pin;
19use std::ptr::NonNull;
20use std::sync::atomic::{AtomicBool, Ordering};
21use std::sync::{Arc, Once, OnceLock};
22
23#[cfg(not(test))]
24#[allow(unused)]
25use std::panic::catch_unwind as handle_panic;
26
27#[cfg(test)]
28#[inline]
29#[allow(unused)]
30fn handle_panic<F: FnOnce() -> R, R>(f: F) -> std::thread::Result<R> {
31 Ok(f())
32}
33
34pub(crate) mod descriptor;
35mod logging;
36
37pub struct HostWrapper<H: HostHandlers> {
48 audio_processor: UnsafeOptionCell<<H as HostHandlers>::AudioProcessor<'static>>,
49 main_thread: OnceCell<<H as HostHandlers>::MainThread<'static>>,
50 shared: <H as HostHandlers>::Shared<'static>,
51
52 init_guard: Once,
54 init_started: AtomicBool,
55 plugin_ptr: OnceLock<NonNull<clap_plugin>>,
56
57 destroy_lock: Arc<DestroyLock>,
59}
60
61unsafe impl<H: HostHandlers> Send for HostWrapper<H> {}
63unsafe impl<H: HostHandlers> Sync for HostWrapper<H> {}
65
66impl<H: HostHandlers> HostWrapper<H> {
67 pub unsafe fn handle<T, F>(host: *const clap_host, handler: F) -> Option<T>
104 where
105 F: FnOnce(&HostWrapper<H>) -> Result<T, HostWrapperError>,
106 {
107 let result = Self::from_raw(host).and_then(|h| {
108 Self::handle_panic(h, |h| {
109 h.ensure_initializing_called();
110 handler(h)
111 })
112 });
113
114 match result {
115 Ok(value) => Some(value),
116 Err(e) => {
117 logging::host_log(host, &e);
118
119 None
120 }
121 }
122 }
123
124 pub unsafe fn handle_main_thread<T, F>(host: *const clap_host, handler: F) -> Option<T>
127 where
128 F: for<'a> FnOnce(&<H as HostHandlers>::MainThread<'a>) -> Result<T, HostWrapperError>,
129 {
130 Self::handle(host, |host| host.on_main_thread(handler).transpose()).flatten()
131 }
132
133 pub(crate) unsafe fn on_main_thread<T>(
136 &self,
137 handler: impl for<'a> FnOnce(&<H as HostHandlers>::MainThread<'a>) -> T,
138 ) -> Option<T> {
139 Some(handler(self.main_thread.get().as_ref()?))
140 }
141
142 #[inline]
157 pub unsafe fn audio_processor(
158 &self,
159 ) -> Result<NonNull<<H as HostHandlers>::AudioProcessor<'_>>, HostWrapperError> {
160 let ptr = self
161 .audio_processor
162 .as_ptr()
163 .ok_or(HostWrapperError::DeactivatedPlugin)?;
164
165 Ok(ptr.cast())
166 }
167
168 #[inline]
170 pub fn shared(&self) -> &<H as HostHandlers>::Shared<'_> {
171 unsafe { shrink_shared_ref::<H>(&self.shared) }
173 }
174
175 pub(crate) fn new<FS, FH>(shared: FS, main_thread: FH) -> Pin<Arc<Self>>
176 where
177 FS: for<'s> FnOnce(&'s ()) -> <H as HostHandlers>::Shared<'s>,
178 FH: for<'s> FnOnce(
179 &'s <H as HostHandlers>::Shared<'s>,
180 ) -> <H as HostHandlers>::MainThread<'s>,
181 {
182 let wrapper = Arc::new(Self {
185 audio_processor: UnsafeOptionCell::new(),
186 main_thread: OnceCell::new(),
187 shared: shared(&()),
188 init_guard: Once::new(),
189 init_started: AtomicBool::new(false),
190 plugin_ptr: OnceLock::new(),
191 destroy_lock: Arc::new(DestroyLock::new()),
192 });
193
194 let main_thread = main_thread(unsafe { extend_shared_ref(&wrapper.shared) });
196
197 let Ok(()) = wrapper.main_thread.set(main_thread) else {
198 unreachable!()
199 };
200
201 unsafe { Pin::new_unchecked(wrapper) }
204 }
205
206 pub(crate) unsafe fn created(&self, instance: NonNull<clap_plugin>) {
210 let _ = self.plugin_ptr.set(instance);
211 }
212
213 pub(crate) unsafe fn instantiated(&self) {
217 self.ensure_initializing_called();
218 let instance = *self.plugin_ptr.get().unwrap();
219
220 self.main_thread
221 .get()
222 .unwrap()
223 .initialized(InitializedPluginHandle::new(
224 self.destroy_lock.clone(),
225 instance,
226 ));
227 }
228
229 pub(crate) fn start_instance_destroy(&self) {
230 self.destroy_lock.start_destroying();
231 }
232
233 #[inline]
237 pub(crate) unsafe fn setup_audio_processor<FA>(
238 &self,
239 audio_processor: FA,
240 ) -> Result<(), PluginInstanceError>
241 where
242 FA: for<'a> FnOnce(
243 &'a <H as HostHandlers>::Shared<'a>,
244 &<H as HostHandlers>::MainThread<'a>,
245 ) -> <H as HostHandlers>::AudioProcessor<'a>,
246 {
247 if self.audio_processor.is_some() {
248 return Err(PluginInstanceError::AlreadyActivatedPlugin);
249 }
250
251 self.audio_processor.put(audio_processor(
252 unsafe { extend_shared_ref(&self.shared) },
254 self.main_thread.get().unwrap(),
257 ));
258 Ok(())
259 }
260
261 #[inline]
265 pub(crate) unsafe fn teardown_audio_processor<T>(
266 &self,
267 drop: impl for<'s> FnOnce(
268 <H as HostHandlers>::AudioProcessor<'s>,
269 &<H as HostHandlers>::MainThread<'s>,
270 ) -> T,
271 ) -> Result<T, PluginInstanceError> {
272 match self.audio_processor.take() {
274 None => Err(PluginInstanceError::DeactivatedPlugin),
275 Some(audio_processor) => Ok(drop(
276 audio_processor,
277 self.main_thread.get().unwrap(),
280 )),
281 }
282 }
283
284 #[inline]
288 pub(crate) fn is_active(&self) -> bool {
289 self.audio_processor.is_some()
290 }
291
292 pub(crate) fn handle_panic<T, F, Pa>(param: Pa, handler: F) -> Result<T, HostWrapperError>
293 where
294 F: FnOnce(Pa) -> Result<T, HostWrapperError>,
295 {
296 handle_panic(AssertUnwindSafe(|| handler(param))).map_err(|_| HostWrapperError::Panic)?
297 }
298
299 unsafe fn from_raw<'a>(raw: *const clap_host) -> Result<&'a Self, HostWrapperError> {
302 raw.as_ref()
303 .ok_or(HostWrapperError::NullHostInstance)?
304 .host_data
305 .cast::<HostWrapper<H>>()
306 .as_ref()
307 .ok_or(HostWrapperError::NullHostData)
308 }
309
310 fn ensure_initializing_called(&self) {
311 let Some(ptr) = self.plugin_ptr.get().copied() else {
313 return;
314 };
315
316 self.init_guard.call_once_force(|_| {
317 let result =
318 self.init_started
319 .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst);
320
321 if result == Ok(false) {
323 let handle =
325 unsafe { InitializingPluginHandle::new(self.destroy_lock.clone(), ptr) };
326 self.shared().initializing(handle);
327 }
328 })
329 }
330}
331
332#[derive(Debug)]
334#[non_exhaustive]
335pub enum HostWrapperError {
336 InvalidParameter(&'static str),
340 NullHostInstance,
342 NullHostData,
344 Panic,
346 DeactivatedPlugin,
348 ErrorFormatError(std::io::Error),
350 Error(Box<dyn Error + 'static>),
352 Message(&'static str),
354}
355
356impl HostWrapperError {
357 fn severity(&self) -> clap_log_severity {
358 match self {
359 HostWrapperError::NullHostInstance => CLAP_LOG_PLUGIN_MISBEHAVING,
360 HostWrapperError::InvalidParameter(_) => CLAP_LOG_PLUGIN_MISBEHAVING,
361 HostWrapperError::NullHostData => CLAP_LOG_HOST_MISBEHAVING,
362 HostWrapperError::Panic => CLAP_LOG_HOST_MISBEHAVING,
363 HostWrapperError::DeactivatedPlugin => CLAP_LOG_PLUGIN_MISBEHAVING,
364 HostWrapperError::ErrorFormatError(_) => CLAP_LOG_HOST_MISBEHAVING,
365 _ => CLAP_LOG_ERROR,
366 }
367 }
368
369 pub fn format_cstr(&self) -> Cow<'static, CStr> {
374 let mut buf = Vec::new();
375 match buf.write_fmt(format_args!("{}", self)) {
376 Ok(()) => {}
377 Err(e) => {
378 if matches!(self, HostWrapperError::ErrorFormatError(_)) {
380 return c"Failed to format error".into();
381 }
382
383 return HostWrapperError::ErrorFormatError(e).format_cstr();
384 }
385 };
386
387 if let Some(idx) = buf.iter().position(|&b| b == 0) {
389 buf.truncate(idx);
390 }
391
392 let Ok(str) = CString::new(buf) else {
394 return c"Error format failure: NulError".into();
395 };
396
397 str.into()
398 }
399}
400
401impl Display for HostWrapperError {
402 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
403 use HostWrapperError::*;
404 match self {
405 NullHostInstance => f.write_str("Host instance pointer is NULL"),
406 NullHostData => f.write_str("Host data pointer is NULL"),
407 InvalidParameter(s) => write!(f, "Invalid parameter: {s}"),
408 Panic => f.write_str("Host callback panicked"),
409 DeactivatedPlugin => {
410 f.write_str("Tried to call an audio-thread method while the plugin was deactivated")
411 }
412 Message(s) => f.write_str(s),
413 Error(e) => Display::fmt(e, f),
414 ErrorFormatError(e) => {
415 write!(f, "Error while formatting host error: '{e}'")
416 }
417 }
418 }
419}
420
421impl From<HostError> for HostWrapperError {
422 #[inline]
423 fn from(value: HostError) -> Self {
424 match value {
425 HostError::Error(e) => Self::Error(e),
426 HostError::Message(e) => Self::Message(e),
427 }
428 }
429}
430
431unsafe fn extend_shared_ref<'a, H: SharedHandler<'a>>(shared: &H) -> &'a H {
434 &*(shared as *const _)
435}
436
437unsafe fn shrink_shared_ref<'a, 'instance, H: HostHandlers>(
440 shared: &'a H::Shared<'instance>,
441) -> &'a H::Shared<'a> {
442 let original_ptr = shared as *const H::Shared<'instance>;
443 let transmuted_ptr: *const H::Shared<'a> = original_ptr.cast();
444
445 &*transmuted_ptr
446}