pipewire_native_spa/interface/
mod.rs

1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: Copyright (c) 2025 Asymptotic Inc.
3// SPDX-FileCopyrightText: Copyright (c) 2025 Arun Raghavan
4
5use std::{
6    collections::HashMap,
7    ffi::{c_void, CStr, CString},
8    pin::Pin,
9    sync::Arc,
10};
11
12use cpu::CpuImpl;
13use ffi::{CInterface, CSupport};
14use log::LogImpl;
15use r#loop::{LoopControlImpl, LoopImpl, LoopUtilsImpl};
16use system::SystemImpl;
17use thread::ThreadUtilsImpl;
18
19use crate::support;
20
21pub mod cpu;
22pub mod ffi;
23pub mod log;
24pub mod r#loop;
25pub mod plugin;
26pub mod system;
27pub mod thread;
28
29/* Well-known interface names */
30pub const CPU: &str = "Spa:Pointer:Interface:CPU";
31pub const LOG: &str = "Spa:Pointer:Interface:Log";
32pub const LOOP: &str = "Spa:Pointer:Interface:Loop";
33pub const LOOP_CONTROL: &str = "Spa:Pointer:Interface:LoopControl";
34pub const LOOP_UTILS: &str = "Spa:Pointer:Interface:LoopUtils";
35pub const SYSTEM: &str = "Spa:Pointer:Interface:System";
36pub const THREAD_UTILS: &str = "Spa:Pointer:Interface:ThreadUtils";
37
38pub struct Support {
39    supports: HashMap<&'static str, Arc<Pin<Box<dyn plugin::Interface>>>>,
40    /* We keep a C-compatible array that won't get moved around, so we can reliably pass it on to
41     * plugins */
42    c_supports: Vec<CSupport>,
43}
44
45unsafe impl Send for Support {}
46unsafe impl Sync for Support {}
47
48impl Default for Support {
49    fn default() -> Self {
50        Support {
51            supports: HashMap::new(),
52            /* Reserve enough space so the array is always valid */
53            c_supports: Vec::with_capacity(16),
54        }
55    }
56}
57
58impl Support {
59    pub fn new() -> Support {
60        Support::default()
61    }
62
63    pub fn c_support(&self) -> &Vec<CSupport> {
64        &self.c_supports
65    }
66
67    fn add_or_update_c(&mut self, name: &str, data: *mut CInterface) {
68        for s in self.c_supports.iter_mut() {
69            let type_ = unsafe { CStr::from_ptr(s.type_).to_str() };
70            if type_ == Ok(name) {
71                s.data = data as *mut c_void;
72                return;
73            }
74        }
75
76        self.c_supports.push(CSupport {
77            type_: support::ffi::c_string(name).into_raw(),
78            data: data as *mut c_void,
79        });
80    }
81
82    pub fn add_interface(&mut self, name: &'static str, iface: Box<dyn plugin::Interface>) {
83        let pin = Box::into_pin(iface);
84        let data = unsafe { pin.make_native() };
85
86        #[allow(clippy::arc_with_non_send_sync)]
87        self.supports.insert(name, Arc::new(pin));
88        self.add_or_update_c(name, data);
89    }
90
91    pub fn get_interface<T>(&self, name: &str) -> Option<Arc<Pin<Box<T>>>>
92    where
93        T: plugin::Interface + 'static,
94    {
95        let iface = self.supports.get(name).cloned();
96
97        iface.and_then(|iface| iface.downcast_arc_pin_box::<T>().ok())
98    }
99}
100
101impl Drop for Support {
102    fn drop(&mut self) {
103        for s in self.c_supports.iter_mut() {
104            unsafe {
105                let type_ = CString::from_raw(s.type_);
106                match type_.to_str().unwrap() {
107                    CPU => <CpuImpl as plugin::Interface>::free_native(s.data as *mut CInterface),
108                    LOOP => <LoopImpl as plugin::Interface>::free_native(s.data as *mut CInterface),
109                    LOOP_CONTROL => <LoopControlImpl as plugin::Interface>::free_native(
110                        s.data as *mut CInterface,
111                    ),
112                    LOOP_UTILS => {
113                        <LoopUtilsImpl as plugin::Interface>::free_native(s.data as *mut CInterface)
114                    }
115                    LOG => <LogImpl as plugin::Interface>::free_native(s.data as *mut CInterface),
116                    SYSTEM => {
117                        <SystemImpl as plugin::Interface>::free_native(s.data as *mut CInterface)
118                    }
119                    THREAD_UTILS => <ThreadUtilsImpl as plugin::Interface>::free_native(
120                        s.data as *mut CInterface,
121                    ),
122                    _ => unreachable!(),
123                }
124            }
125        }
126    }
127}