pipewire/
factory.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 Factory {
19    proxy: Proxy,
20}
21
22impl ProxyT for Factory {
23    fn type_() -> ObjectType {
24        ObjectType::Factory
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 Factory {
44    // TODO: add non-local version when we'll bind pw_thread_loop_start()
45    #[must_use]
46    pub fn add_listener_local(&self) -> FactoryListenerLocalBuilder<'_> {
47        FactoryListenerLocalBuilder {
48            factory: 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(&FactoryInfoRef)>>,
58}
59
60pub struct FactoryListenerLocalBuilder<'a> {
61    factory: &'a Factory,
62    cbs: ListenerLocalCallbacks,
63}
64
65#[repr(transparent)]
66pub struct FactoryInfoRef(pw_sys::pw_factory_info);
67
68impl FactoryInfoRef {
69    pub fn as_raw(&self) -> &pw_sys::pw_factory_info {
70        &self.0
71    }
72
73    pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_factory_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 type_(&self) -> ObjectType {
82        ObjectType::from_str(unsafe { CStr::from_ptr(self.0.type_).to_str().unwrap() })
83    }
84
85    pub fn version(&self) -> u32 {
86        self.0.version
87    }
88
89    pub fn change_mask(&self) -> FactoryChangeMask {
90        FactoryChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
91    }
92
93    pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
94        let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
95        ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
96    }
97}
98
99impl fmt::Debug for FactoryInfoRef {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        f.debug_struct("FactoryInfoRef")
102            .field("id", &self.id())
103            .field("type", &self.type_())
104            .field("version", &self.version())
105            .field("change_mask", &self.change_mask())
106            .field("props", &self.props())
107            .finish()
108    }
109}
110
111pub struct FactoryInfo {
112    ptr: ptr::NonNull<pw_sys::pw_factory_info>,
113}
114
115impl FactoryInfo {
116    pub fn new(ptr: ptr::NonNull<pw_sys::pw_factory_info>) -> Self {
117        Self { ptr }
118    }
119
120    pub fn from_raw(raw: *mut pw_sys::pw_factory_info) -> Self {
121        Self {
122            ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
123        }
124    }
125
126    pub fn into_raw(self) -> *mut pw_sys::pw_factory_info {
127        std::mem::ManuallyDrop::new(self).ptr.as_ptr()
128    }
129}
130
131impl Drop for FactoryInfo {
132    fn drop(&mut self) {
133        unsafe { pw_sys::pw_factory_info_free(self.ptr.as_ptr()) }
134    }
135}
136
137impl std::ops::Deref for FactoryInfo {
138    type Target = FactoryInfoRef;
139
140    fn deref(&self) -> &Self::Target {
141        unsafe { self.ptr.cast::<FactoryInfoRef>().as_ref() }
142    }
143}
144
145impl AsRef<FactoryInfoRef> for FactoryInfo {
146    fn as_ref(&self) -> &FactoryInfoRef {
147        self.deref()
148    }
149}
150
151bitflags! {
152    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
153    pub struct FactoryChangeMask: u64 {
154        const PROPS = pw_sys::PW_FACTORY_CHANGE_MASK_PROPS as u64;
155    }
156}
157
158impl fmt::Debug for FactoryInfo {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.debug_struct("FactoryInfo")
161            .field("id", &self.id())
162            .field("type", &self.type_())
163            .field("version", &self.version())
164            .field("change_mask", &self.change_mask())
165            .field("props", &self.props())
166            .finish()
167    }
168}
169
170pub struct FactoryListener {
171    // Need to stay allocated while the listener is registered
172    #[allow(dead_code)]
173    events: Pin<Box<pw_sys::pw_factory_events>>,
174    listener: Pin<Box<spa_sys::spa_hook>>,
175    #[allow(dead_code)]
176    data: Box<ListenerLocalCallbacks>,
177}
178
179impl Listener for FactoryListener {}
180
181impl Drop for FactoryListener {
182    fn drop(&mut self) {
183        spa::utils::hook::remove(*self.listener);
184    }
185}
186
187impl<'a> FactoryListenerLocalBuilder<'a> {
188    #[must_use]
189    pub fn info<F>(mut self, info: F) -> Self
190    where
191        F: Fn(&FactoryInfoRef) + 'static,
192    {
193        self.cbs.info = Some(Box::new(info));
194        self
195    }
196
197    #[must_use]
198    pub fn register(self) -> FactoryListener {
199        unsafe extern "C" fn factory_events_info(
200            data: *mut c_void,
201            info: *const pw_sys::pw_factory_info,
202        ) {
203            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
204            let info =
205                ptr::NonNull::new(info as *mut pw_sys::pw_factory_info).expect("info is NULL");
206            let info = info.cast::<FactoryInfoRef>().as_ref();
207            callbacks.info.as_ref().unwrap()(info);
208        }
209
210        let e = unsafe {
211            let mut e: Pin<Box<pw_sys::pw_factory_events>> = Box::pin(mem::zeroed());
212            e.version = pw_sys::PW_VERSION_FACTORY_EVENTS;
213
214            if self.cbs.info.is_some() {
215                e.info = Some(factory_events_info);
216            }
217
218            e
219        };
220
221        let (listener, data) = unsafe {
222            let factory = &self.factory.proxy.as_ptr();
223
224            let data = Box::into_raw(Box::new(self.cbs));
225            let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
226            let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
227
228            spa_interface_call_method!(
229                factory,
230                pw_sys::pw_factory_methods,
231                add_listener,
232                listener_ptr.cast(),
233                e.as_ref().get_ref(),
234                data as *mut _
235            );
236
237            (listener, Box::from_raw(data))
238        };
239
240        FactoryListener {
241            events: e,
242            listener,
243            data,
244        }
245    }
246}