clack_plugin/host.rs
1//! Types and handles for plugins to interact with the host.
2
3use clack_common::extensions::{Extension, HostExtensionSide, RawExtension};
4use clack_common::utils::ClapVersion;
5use clap_sys::host::clap_host;
6use std::ffi::CStr;
7use std::marker::PhantomData;
8use std::ops::Deref;
9use std::ptr::NonNull;
10
11/// Various information about the host, provided at plugin instantiation time.
12#[derive(Copy, Clone)]
13#[repr(transparent)]
14pub struct HostInfo<'a> {
15 raw: NonNull<clap_host>,
16 _lifetime: PhantomData<&'a clap_host>,
17}
18
19impl<'a> HostInfo<'a> {
20 /// Creates a new [`HostInfo`] type from a given raw, C FFI compatible pointer.
21 ///
22 /// # Safety
23 /// Pointer must be valid for the duration of the `'a` lifetime. Moreover, the contents of
24 /// the `clap_host` struct must all also be valid.
25 #[inline]
26 pub const unsafe fn from_raw(raw: NonNull<clap_host>) -> Self {
27 Self {
28 raw,
29 _lifetime: PhantomData,
30 }
31 }
32
33 /// The [`ClapVersion`] the host uses.
34 #[inline]
35 pub const fn clap_version(&self) -> ClapVersion {
36 ClapVersion::from_raw(self.as_raw().clap_version)
37 }
38
39 /// A user-friendly name for the host (e.g. "Bitwig Studio").
40 ///
41 /// This should always be set by the host.
42 pub const fn name(&self) -> Option<&'a CStr> {
43 let Some(ptr) = NonNull::new(self.as_raw().name as *mut _) else {
44 return None;
45 };
46 // SAFETY: this type ensures the pointers are valid
47 Some(unsafe { CStr::from_ptr(ptr.as_ptr()) })
48 }
49
50 /// The host's vendor (e.g. "Bitwig GmbH").
51 ///
52 /// This field is optional.
53 pub const fn vendor(&self) -> Option<&'a CStr> {
54 let Some(ptr) = NonNull::new(self.as_raw().vendor as *mut _) else {
55 return None;
56 };
57 // SAFETY: this type ensures the pointers are valid
58 Some(unsafe { CStr::from_ptr(ptr.as_ptr()) })
59 }
60
61 /// A URL to the host's webpage.
62 ///
63 /// This field is optional.
64 pub const fn url(&self) -> Option<&'a CStr> {
65 let Some(ptr) = NonNull::new(self.as_raw().url as *mut _) else {
66 return None;
67 };
68 // SAFETY: this type ensures the pointers are valid
69 Some(unsafe { CStr::from_ptr(ptr.as_ptr()) })
70 }
71
72 /// A version string for the host (e.g. "4.3").
73 ///
74 /// This should always be set by the host.
75 pub const fn version(&self) -> Option<&'a CStr> {
76 let Some(ptr) = NonNull::new(self.as_raw().version as *mut _) else {
77 return None;
78 };
79 // SAFETY: this type ensures the pointers are valid
80 Some(unsafe { CStr::from_ptr(ptr.as_ptr()) })
81 }
82
83 /// Retrieves the host's pointer to the given [extension type](Extension) `E`.
84 ///
85 /// This returns `None` if the host does not support the given extension.
86 ///
87 /// # Example
88 ///
89 /// ```
90 /// use clack_extensions::log::{HostLog, LogSeverity};
91 /// use clack_plugin::host::HostInfo;
92 ///
93 /// # fn foo(info: HostInfo) {
94 /// let info: HostInfo = /* ... */
95 /// # info;
96 /// if let Some(log) = info.get_extension::<HostLog>() {
97 /// // The log extension is supported by this host
98 /// } else {
99 /// // The log extension is not supported by this host
100 /// }
101 /// # }
102 /// ```
103 pub fn get_extension<E: Extension<ExtensionSide = HostExtensionSide>>(&self) -> Option<E> {
104 let identifier = const { E::IDENTIFIERS.first().unwrap() };
105 // SAFETY: this type ensures the function pointers are valid
106 let ext = unsafe { self.as_raw().get_extension?(self.raw.as_ptr(), identifier.as_ptr()) };
107
108 let ext = NonNull::new(ext as *mut _)?;
109 // SAFETY: The CLAP spec guarantees that the extension lives as long as the instance.
110 let raw = unsafe { RawExtension::from_raw_host_extension(ext, self.raw) };
111
112 // SAFETY: pointer comes from the associated E::IDENTIFIER.
113 unsafe { Some(E::from_raw(raw)) }
114 }
115
116 /// # Safety
117 /// Some functions exposed by [`HostSharedHandle`] cannot be called until plugin is initializing
118 #[inline]
119 pub(crate) const unsafe fn to_handle(self) -> HostSharedHandle<'a> {
120 HostSharedHandle {
121 raw: self.raw,
122 _lifetime: PhantomData,
123 }
124 }
125
126 /// Returns a raw, C FFI-compatible reference to the host handle.
127 #[inline]
128 pub const fn as_raw(&self) -> &'a clap_host {
129 // SAFETY: this type ensures the raw pointer is valid
130 unsafe { self.raw.as_ref() }
131 }
132}
133
134/// A thread-safe handle to the host.
135///
136/// This can be used to fetch information about the host, scan available extensions, or perform
137/// requests to the host.
138#[derive(Copy, Clone)]
139#[repr(transparent)]
140pub struct HostSharedHandle<'a> {
141 raw: NonNull<clap_host>,
142 _lifetime: PhantomData<&'a clap_host>,
143}
144
145// SAFETY: this type only safely exposes the thread-safe operations of clap_host
146unsafe impl Send for HostSharedHandle<'_> {}
147// SAFETY: this type only safely exposes the thread-safe operations of clap_host
148unsafe impl Sync for HostSharedHandle<'_> {}
149
150impl<'a> HostSharedHandle<'a> {
151 /// Returns the host's information.
152 #[inline]
153 pub const fn info(&self) -> HostInfo<'a> {
154 HostInfo {
155 raw: self.raw,
156 _lifetime: PhantomData,
157 }
158 }
159
160 /// Returns a raw, C FFI-compatible reference to the host handle.
161 #[inline]
162 pub const fn as_raw(&self) -> &'a clap_host {
163 // SAFETY: this type enforces the pointer is valid for 'a
164 unsafe { self.raw.as_ref() }
165 }
166
167 /// Returns this handle as a reference to the host's information.
168 #[inline]
169 pub const fn as_info(&self) -> &HostInfo<'a> {
170 // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
171 unsafe { &*(self as *const Self as *const HostInfo<'a>) }
172 }
173
174 /// Requests the host to [deactivate](crate::plugin::PluginAudioProcessor::deactivate) and then
175 /// [re-activate](crate::plugin::PluginAudioProcessor::activate) the plugin.
176 /// The operation may be delayed by the host.
177 #[inline]
178 pub fn request_restart(&self) {
179 if let Some(request_restart) = self.as_raw().request_restart {
180 // SAFETY: field is guaranteed to be correct by host. Lifetime is enforced by 'a
181 unsafe { request_restart(self.raw.as_ptr()) }
182 }
183 }
184
185 /// Requests the host to [activate](crate::plugin::PluginAudioProcessor::activate) the plugin,
186 /// and start audio processing.
187 /// This is useful if you have external IO and need to wake the plugin up from "sleep".
188 #[inline]
189 pub fn request_process(&self) {
190 if let Some(request_process) = self.as_raw().request_process {
191 // SAFETY: field is guaranteed to be correct by host. Lifetime is enforced by 'a
192 unsafe { request_process(self.raw.as_ptr()) }
193 }
194 }
195
196 /// Requests the host to schedule a call to the
197 /// [on_main_thread](crate::plugin::PluginMainThread::on_main_thread) method on the main thread.
198 #[inline]
199 pub fn request_callback(&self) {
200 if let Some(request_callback) = self.as_raw().request_callback {
201 // SAFETY: field is guaranteed to be correct by host. Lifetime is enforced by 'a
202 unsafe { request_callback(self.raw.as_ptr()) }
203 }
204 }
205
206 /// Unsafely creates a main-thread host handle from this thread-safe handle.
207 ///
208 /// # Safety
209 ///
210 /// Callers *MUST* ensure this is only called on the main thread, and that they have exclusive (&mut) access.
211 #[inline]
212 pub const unsafe fn as_main_thread_unchecked(&self) -> HostMainThreadHandle<'a> {
213 HostMainThreadHandle {
214 raw: self.raw,
215 _lifetime: PhantomData,
216 }
217 }
218
219 /// Unsafely creates an audio-processor host handle from this thread-safe handle.
220 ///
221 /// # Safety
222 ///
223 /// Callers *MUST* ensure this is only called on the audio thread, and that they have exclusive (&mut) access.
224 #[inline]
225 pub const unsafe fn as_audio_processor_unchecked(&self) -> HostAudioProcessorHandle<'a> {
226 HostAudioProcessorHandle {
227 raw: self.raw,
228 _lifetime: PhantomData,
229 }
230 }
231
232 /// Safely dereferences a [`RawExtension`] pointer produced by this host.
233 ///
234 /// See the documentation of the [`RawExtension`] type for more information about how this works
235 /// internally.
236 ///
237 /// # Panics
238 ///
239 /// This method will panic if the given extension pointer does not match the host
240 /// this handle came from.
241 #[inline]
242 pub fn use_extension<E: Sized>(&self, extension: &RawExtension<HostExtensionSide, E>) -> &'a E {
243 if self.raw != extension.host_ptr() {
244 mismatched_instance();
245 }
246
247 // SAFETY: the RawExtension type enforces the pointee is valid for as long as the matching
248 // instance is still alive.
249 unsafe { extension.as_ptr().as_ref() }
250 }
251
252 /// Creates a [`HostSharedHandle`] from a raw `clap_host` pointer.
253 ///
254 /// # Safety
255 ///
256 ///
257 #[inline]
258 pub unsafe fn from_raw(raw: NonNull<clap_host>) -> Self {
259 Self {
260 raw,
261 _lifetime: PhantomData,
262 }
263 }
264
265 /// Returns a [`HostSharedHandle`] that points to the same host, but with an arbitrary lifetime
266 /// instead of the host's.
267 ///
268 /// This can be useful in situations where you need to use host callbacks in a container that
269 /// does not let you use proper lifetimes, but you can still guarantee that host callbacks won't
270 /// be called after the host's `'a` lifetime.
271 ///
272 /// # Safety
273 ///
274 /// The returned handle's lifetime is completely meaningless here.
275 /// Proper care muse be taken in order to ensure *no* methods on this type are used beyond the
276 /// original host `'a` lifetime.
277 #[inline]
278 pub unsafe fn with_arbitrary_lifetime<'b>(&self) -> HostSharedHandle<'b> {
279 HostSharedHandle {
280 raw: self.raw,
281 _lifetime: PhantomData,
282 }
283 }
284}
285
286impl<'a> From<HostSharedHandle<'a>> for HostInfo<'a> {
287 #[inline]
288 fn from(h: HostSharedHandle<'a>) -> Self {
289 h.info()
290 }
291}
292
293impl<'a> Deref for HostSharedHandle<'a> {
294 type Target = HostInfo<'a>;
295
296 #[inline]
297 fn deref(&self) -> &Self::Target {
298 self.as_info()
299 }
300}
301
302/// A main-thread handle to the host.
303///
304/// This can be used to perform requests to the host that can only be made from the main thread.
305#[repr(transparent)]
306pub struct HostMainThreadHandle<'a> {
307 raw: NonNull<clap_host>,
308 _lifetime: PhantomData<&'a clap_host>,
309}
310
311impl<'a> HostMainThreadHandle<'a> {
312 /// Gets a thread-safe host handle from this handle.
313 #[inline]
314 pub const fn shared(&self) -> HostSharedHandle<'a> {
315 HostSharedHandle {
316 raw: self.raw,
317 _lifetime: PhantomData,
318 }
319 }
320
321 /// Returns this handle as a reference to a thread-safe host handle from this handle.
322 #[inline]
323 pub const fn as_shared(&self) -> &HostSharedHandle<'a> {
324 // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
325 unsafe { &*(self as *const Self as *const HostSharedHandle<'a>) }
326 }
327
328 /// Returns a raw, C FFI-compatible reference to the host handle.
329 #[inline]
330 pub const fn as_raw(&self) -> &'a clap_host {
331 // SAFETY: this type enforces the pointer is valid for 'a
332 unsafe { self.raw.as_ref() }
333 }
334
335 /// Creates a [`HostMainThreadHandle`] from a raw `clap_host` pointer.
336 ///
337 /// # Safety
338 ///
339 ///
340 #[inline]
341 pub unsafe fn from_raw(raw: NonNull<clap_host>) -> Self {
342 Self {
343 raw,
344 _lifetime: PhantomData,
345 }
346 }
347
348 /// Returns a [`HostMainThreadHandle`] that points to the same host, but with an arbitrary lifetime
349 /// instead of the host's.
350 ///
351 /// This can be useful in situations where you need to use host callbacks in a container that
352 /// does not let you use proper lifetimes, but you can still guarantee that host callbacks won't
353 /// be called after the host's `'a` lifetime.
354 ///
355 /// # Safety
356 ///
357 /// The returned handle's lifetime is completely meaningless here.
358 /// Proper care muse be taken in order to ensure *no* methods on this type are used beyond the
359 /// original host `'a` lifetime.
360 #[inline]
361 pub unsafe fn with_arbitrary_lifetime<'b>(&self) -> HostMainThreadHandle<'b> {
362 HostMainThreadHandle {
363 raw: self.raw,
364 _lifetime: PhantomData,
365 }
366 }
367}
368
369impl<'a> From<HostMainThreadHandle<'a>> for HostSharedHandle<'a> {
370 #[inline]
371 fn from(h: HostMainThreadHandle<'a>) -> Self {
372 h.shared()
373 }
374}
375
376impl<'a> Deref for HostMainThreadHandle<'a> {
377 type Target = HostSharedHandle<'a>;
378
379 #[inline]
380 fn deref(&self) -> &Self::Target {
381 self.as_shared()
382 }
383}
384
385/// An audio-processor handle to the host.
386///
387/// This can be used to perform requests to the host that can only be made from the audio thread.
388#[repr(transparent)]
389pub struct HostAudioProcessorHandle<'a> {
390 raw: NonNull<clap_host>,
391 _lifetime: PhantomData<&'a clap_host>,
392}
393
394// SAFETY: this type only exposes the audio-thread-safe (Send) operation of clap_host
395unsafe impl Send for HostAudioProcessorHandle<'_> {}
396
397impl<'a> HostAudioProcessorHandle<'a> {
398 /// Gets a thread-safe host handle from this handle.
399 #[inline]
400 pub const fn shared(&self) -> HostSharedHandle<'a> {
401 HostSharedHandle {
402 raw: self.raw,
403 _lifetime: PhantomData,
404 }
405 }
406
407 /// Returns a raw, C FFI-compatible reference to the host handle.
408 #[inline]
409 pub const fn as_raw(&self) -> &'a clap_host {
410 // SAFETY: this type enforces the pointer is valid for 'a
411 unsafe { self.raw.as_ref() }
412 }
413
414 /// Returns this handle as a reference to a thread-safe host handle from this handle.
415 #[inline]
416 pub const fn as_shared(&self) -> &HostSharedHandle<'a> {
417 // SAFETY: this cast is valid since both types are just a NonNull<clap_host> and repr(transparent)
418 unsafe { &*(self as *const Self as *const HostSharedHandle<'a>) }
419 }
420
421 /// Creates a [`HostAudioProcessorHandle`] from a raw `clap_host` pointer.
422 ///
423 /// # Safety
424 ///
425 ///
426 #[inline]
427 pub unsafe fn from_raw(raw: NonNull<clap_host>) -> Self {
428 Self {
429 raw,
430 _lifetime: PhantomData,
431 }
432 }
433
434 /// Returns a [`HostAudioProcessorHandle`] that points to the same host, but with an arbitrary lifetime
435 /// instead of the host's.
436 ///
437 /// This can be useful in situations where you need to use host callbacks in a container that
438 /// does not let you use proper lifetimes, but you can still guarantee that host callbacks won't
439 /// be called after the host's `'a` lifetime.
440 ///
441 /// # Safety
442 ///
443 /// The returned handle's lifetime is completely meaningless here.
444 /// Proper care muse be taken in order to ensure *no* methods on this type are used beyond the
445 /// original host `'a` lifetime.
446 #[inline]
447 pub unsafe fn with_arbitrary_lifetime<'b>(&self) -> HostAudioProcessorHandle<'b> {
448 HostAudioProcessorHandle {
449 raw: self.raw,
450 _lifetime: PhantomData,
451 }
452 }
453}
454
455impl<'a> From<HostAudioProcessorHandle<'a>> for HostSharedHandle<'a> {
456 #[inline]
457 fn from(h: HostAudioProcessorHandle<'a>) -> Self {
458 h.shared()
459 }
460}
461
462impl<'a> Deref for HostAudioProcessorHandle<'a> {
463 type Target = HostSharedHandle<'a>;
464
465 #[inline]
466 fn deref(&self) -> &Self::Target {
467 self.as_shared()
468 }
469}
470
471#[inline(never)]
472#[cold]
473const fn mismatched_instance() -> ! {
474 panic!("Given host handle doesn't match the extension pointer it was used on.")
475}