pipewire/
module.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4use bitflags::bitflags;
5use libc::c_void;
6use std::ops::Deref;
7use std::pin::Pin;
8use std::{ffi::CStr, ptr};
9use std::{fmt, mem};
10
11use crate::{
12    proxy::{Listener, Proxy, ProxyT},
13    types::ObjectType,
14};
15use spa::spa_interface_call_method;
16
17#[derive(Debug)]
18pub struct Module {
19    proxy: Proxy,
20}
21
22impl ProxyT for Module {
23    fn type_() -> ObjectType {
24        ObjectType::Module
25    }
26
27    fn upcast(self) -> Proxy {
28        self.proxy
29    }
30
31    fn upcast_ref(&self) -> &Proxy {
32        &self.proxy
33    }
34
35    unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
36    where
37        Self: Sized,
38    {
39        Self { proxy }
40    }
41}
42
43impl Module {
44    // TODO: add non-local version when we'll bind pw_thread_loop_start()
45    #[must_use]
46    pub fn add_listener_local(&self) -> ModuleListenerLocalBuilder<'_> {
47        ModuleListenerLocalBuilder {
48            module: self,
49            cbs: ListenerLocalCallbacks::default(),
50        }
51    }
52}
53
54#[derive(Default)]
55struct ListenerLocalCallbacks {
56    #[allow(clippy::type_complexity)]
57    info: Option<Box<dyn Fn(&ModuleInfoRef)>>,
58}
59
60pub struct ModuleListenerLocalBuilder<'a> {
61    module: &'a Module,
62    cbs: ListenerLocalCallbacks,
63}
64
65#[repr(transparent)]
66pub struct ModuleInfoRef(pw_sys::pw_module_info);
67
68impl ModuleInfoRef {
69    pub fn as_raw(&self) -> &pw_sys::pw_module_info {
70        &self.0
71    }
72
73    pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_module_info {
74        std::ptr::addr_of!(self.0).cast_mut()
75    }
76
77    pub fn id(&self) -> u32 {
78        self.0.id
79    }
80
81    pub fn name(&self) -> &str {
82        unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
83    }
84
85    pub fn filename(&self) -> &str {
86        unsafe { CStr::from_ptr(self.0.name).to_str().unwrap() }
87    }
88
89    pub fn args(&self) -> Option<&str> {
90        let args = self.0.args;
91        if args.is_null() {
92            None
93        } else {
94            Some(unsafe { CStr::from_ptr(args).to_str().unwrap() })
95        }
96    }
97
98    pub fn change_mask(&self) -> ModuleChangeMask {
99        ModuleChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
100    }
101
102    pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
103        let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
104        ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
105    }
106}
107
108impl fmt::Debug for ModuleInfoRef {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("ModuleInfoRef")
111            .field("id", &self.id())
112            .field("filename", &self.filename())
113            .field("args", &self.args())
114            .field("change_mask", &self.change_mask())
115            .field("props", &self.props())
116            .finish()
117    }
118}
119
120pub struct ModuleInfo {
121    ptr: ptr::NonNull<pw_sys::pw_module_info>,
122}
123
124impl ModuleInfo {
125    pub fn new(ptr: ptr::NonNull<pw_sys::pw_module_info>) -> Self {
126        Self { ptr }
127    }
128
129    pub fn from_raw(raw: *mut pw_sys::pw_module_info) -> Self {
130        Self {
131            ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
132        }
133    }
134
135    pub fn into_raw(self) -> *mut pw_sys::pw_module_info {
136        std::mem::ManuallyDrop::new(self).ptr.as_ptr()
137    }
138}
139
140impl Drop for ModuleInfo {
141    fn drop(&mut self) {
142        unsafe { pw_sys::pw_module_info_free(self.ptr.as_ptr()) }
143    }
144}
145
146impl std::ops::Deref for ModuleInfo {
147    type Target = ModuleInfoRef;
148
149    fn deref(&self) -> &Self::Target {
150        unsafe { self.ptr.cast::<ModuleInfoRef>().as_ref() }
151    }
152}
153
154impl AsRef<ModuleInfoRef> for ModuleInfo {
155    fn as_ref(&self) -> &ModuleInfoRef {
156        self.deref()
157    }
158}
159
160bitflags! {
161    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
162    pub struct ModuleChangeMask: u64 {
163        const PROPS = pw_sys::PW_MODULE_CHANGE_MASK_PROPS as u64;
164    }
165}
166
167impl fmt::Debug for ModuleInfo {
168    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169        f.debug_struct("ModuleInfo")
170            .field("id", &self.id())
171            .field("filename", &self.filename())
172            .field("args", &self.args())
173            .field("change_mask", &self.change_mask())
174            .field("props", &self.props())
175            .finish()
176    }
177}
178
179pub struct ModuleListener {
180    // Need to stay allocated while the listener is registered
181    #[allow(dead_code)]
182    events: Pin<Box<pw_sys::pw_module_events>>,
183    listener: Pin<Box<spa_sys::spa_hook>>,
184    #[allow(dead_code)]
185    data: Box<ListenerLocalCallbacks>,
186}
187
188impl Listener for ModuleListener {}
189
190impl Drop for ModuleListener {
191    fn drop(&mut self) {
192        spa::utils::hook::remove(*self.listener);
193    }
194}
195
196impl<'a> ModuleListenerLocalBuilder<'a> {
197    #[must_use]
198    pub fn info<F>(mut self, info: F) -> Self
199    where
200        F: Fn(&ModuleInfoRef) + 'static,
201    {
202        self.cbs.info = Some(Box::new(info));
203        self
204    }
205
206    #[must_use]
207    pub fn register(self) -> ModuleListener {
208        unsafe extern "C" fn module_events_info(
209            data: *mut c_void,
210            info: *const pw_sys::pw_module_info,
211        ) {
212            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
213            let info =
214                ptr::NonNull::new(info as *mut pw_sys::pw_module_info).expect("info is NULL");
215            let info = info.cast::<ModuleInfoRef>().as_ref();
216            callbacks.info.as_ref().unwrap()(info);
217        }
218
219        let e = unsafe {
220            let mut e: Pin<Box<pw_sys::pw_module_events>> = Box::pin(mem::zeroed());
221            e.version = pw_sys::PW_VERSION_MODULE_EVENTS;
222
223            if self.cbs.info.is_some() {
224                e.info = Some(module_events_info);
225            }
226
227            e
228        };
229
230        let (listener, data) = unsafe {
231            let module = &self.module.proxy.as_ptr();
232
233            let data = Box::into_raw(Box::new(self.cbs));
234            let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
235            let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
236
237            spa_interface_call_method!(
238                module,
239                pw_sys::pw_module_methods,
240                add_listener,
241                listener_ptr.cast(),
242                e.as_ref().get_ref(),
243                data as *mut _
244            );
245
246            (listener, Box::from_raw(data))
247        };
248
249        ModuleListener {
250            events: e,
251            listener,
252            data,
253        }
254    }
255}