pipewire/
factory.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4//! Factories are objects that create other objects.
5//!
6//! This module contains wrappers for [`pw_factory`](pw_sys::pw_factory) and related items.
7
8use bitflags::bitflags;
9use libc::c_void;
10use std::ops::Deref;
11use std::pin::Pin;
12use std::{ffi::CStr, ptr};
13use std::{fmt, mem};
14
15use crate::{
16    proxy::{Listener, Proxy, ProxyT},
17    types::ObjectType,
18};
19use spa::spa_interface_call_method;
20
21/// A [proxy][Proxy] to a [factory](self).
22#[derive(Debug)]
23pub struct Factory {
24    proxy: Proxy,
25}
26
27impl ProxyT for Factory {
28    fn type_() -> ObjectType {
29        ObjectType::Factory
30    }
31
32    fn upcast(self) -> Proxy {
33        self.proxy
34    }
35
36    fn upcast_ref(&self) -> &Proxy {
37        &self.proxy
38    }
39
40    unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
41    where
42        Self: Sized,
43    {
44        Self { proxy }
45    }
46}
47
48impl Factory {
49    // TODO: add non-local version when we'll bind pw_thread_loop_start()
50    #[must_use = "Use the builder to register event callbacks"]
51    pub fn add_listener_local(&self) -> FactoryListenerLocalBuilder<'_> {
52        FactoryListenerLocalBuilder {
53            factory: self,
54            cbs: ListenerLocalCallbacks::default(),
55        }
56    }
57}
58
59#[derive(Default)]
60struct ListenerLocalCallbacks {
61    #[allow(clippy::type_complexity)]
62    info: Option<Box<dyn Fn(&FactoryInfoRef)>>,
63}
64
65/// A builder for registering factory event callbacks.
66///
67/// Use [`Factory::add_listener_local`] to create this and register callbacks that will be called when events of interest occur.
68/// After adding callbacks, use [`register`](Self::register) to get back a [`FactoryListener`].
69///
70/// # Examples
71/// ```
72/// # use pipewire::factory::Factory;
73/// # fn example(factory: Factory) {
74/// let factory_listener = factory.add_listener_local()
75///     .info(|info| println!("New factory info: {info:?}"))
76///     .register();
77/// # }
78/// ```
79pub struct FactoryListenerLocalBuilder<'a> {
80    factory: &'a Factory,
81    cbs: ListenerLocalCallbacks,
82}
83
84#[repr(transparent)]
85pub struct FactoryInfoRef(pw_sys::pw_factory_info);
86
87impl FactoryInfoRef {
88    pub fn as_raw(&self) -> &pw_sys::pw_factory_info {
89        &self.0
90    }
91
92    pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_factory_info {
93        std::ptr::addr_of!(self.0).cast_mut()
94    }
95
96    pub fn id(&self) -> u32 {
97        self.0.id
98    }
99
100    pub fn type_(&self) -> ObjectType {
101        ObjectType::from_str(unsafe { CStr::from_ptr(self.0.type_).to_str().unwrap() })
102    }
103
104    pub fn version(&self) -> u32 {
105        self.0.version
106    }
107
108    pub fn change_mask(&self) -> FactoryChangeMask {
109        FactoryChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
110    }
111
112    pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
113        let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
114        ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
115    }
116}
117
118impl fmt::Debug for FactoryInfoRef {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.debug_struct("FactoryInfoRef")
121            .field("id", &self.id())
122            .field("type", &self.type_())
123            .field("version", &self.version())
124            .field("change_mask", &self.change_mask())
125            .field("props", &self.props())
126            .finish()
127    }
128}
129
130pub struct FactoryInfo {
131    ptr: ptr::NonNull<pw_sys::pw_factory_info>,
132}
133
134impl FactoryInfo {
135    pub fn new(ptr: ptr::NonNull<pw_sys::pw_factory_info>) -> Self {
136        Self { ptr }
137    }
138
139    pub fn from_raw(raw: *mut pw_sys::pw_factory_info) -> Self {
140        Self {
141            ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
142        }
143    }
144
145    pub fn into_raw(self) -> *mut pw_sys::pw_factory_info {
146        std::mem::ManuallyDrop::new(self).ptr.as_ptr()
147    }
148}
149
150impl Drop for FactoryInfo {
151    fn drop(&mut self) {
152        unsafe { pw_sys::pw_factory_info_free(self.ptr.as_ptr()) }
153    }
154}
155
156impl std::ops::Deref for FactoryInfo {
157    type Target = FactoryInfoRef;
158
159    fn deref(&self) -> &Self::Target {
160        unsafe { self.ptr.cast::<FactoryInfoRef>().as_ref() }
161    }
162}
163
164impl AsRef<FactoryInfoRef> for FactoryInfo {
165    fn as_ref(&self) -> &FactoryInfoRef {
166        self.deref()
167    }
168}
169
170bitflags! {
171    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
172    pub struct FactoryChangeMask: u64 {
173        const PROPS = pw_sys::PW_FACTORY_CHANGE_MASK_PROPS as u64;
174    }
175}
176
177impl fmt::Debug for FactoryInfo {
178    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179        f.debug_struct("FactoryInfo")
180            .field("id", &self.id())
181            .field("type", &self.type_())
182            .field("version", &self.version())
183            .field("change_mask", &self.change_mask())
184            .field("props", &self.props())
185            .finish()
186    }
187}
188
189/// An owned listener for factory events.
190///
191/// This is created by [`FactoryListenerLocalBuilder`] and will receive events as long as it is alive.
192/// When this gets dropped, the listener gets unregistered and no events will be received by it.
193#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
194pub struct FactoryListener {
195    // Need to stay allocated while the listener is registered
196    #[allow(dead_code)]
197    events: Pin<Box<pw_sys::pw_factory_events>>,
198    listener: Pin<Box<spa_sys::spa_hook>>,
199    #[allow(dead_code)]
200    data: Box<ListenerLocalCallbacks>,
201}
202
203impl Listener for FactoryListener {}
204
205impl Drop for FactoryListener {
206    fn drop(&mut self) {
207        spa::utils::hook::remove(*self.listener);
208    }
209}
210
211impl<'a> FactoryListenerLocalBuilder<'a> {
212    /// Set the factory `info` event callback of the listener.
213    ///
214    /// # Callback parameters
215    /// `info`: Info about the factory
216    ///
217    /// # Examples
218    /// ```
219    /// # use pipewire::factory::Factory;
220    /// # fn example(factory: Factory) {
221    /// let factory_listener = factory.add_listener_local()
222    ///     .info(|info| println!("New factory info: {info:?}"))
223    ///     .register();
224    /// # }
225    /// ```
226    #[must_use = "Call `.register()` to start receiving events"]
227    pub fn info<F>(mut self, info: F) -> Self
228    where
229        F: Fn(&FactoryInfoRef) + 'static,
230    {
231        self.cbs.info = Some(Box::new(info));
232        self
233    }
234
235    /// Subscribe to events and register any provided callbacks.
236    pub fn register(self) -> FactoryListener {
237        unsafe extern "C" fn factory_events_info(
238            data: *mut c_void,
239            info: *const pw_sys::pw_factory_info,
240        ) {
241            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
242            let info =
243                ptr::NonNull::new(info as *mut pw_sys::pw_factory_info).expect("info is NULL");
244            let info = info.cast::<FactoryInfoRef>().as_ref();
245            callbacks.info.as_ref().unwrap()(info);
246        }
247
248        let e = unsafe {
249            let mut e: Pin<Box<pw_sys::pw_factory_events>> = Box::pin(mem::zeroed());
250            e.version = pw_sys::PW_VERSION_FACTORY_EVENTS;
251
252            if self.cbs.info.is_some() {
253                e.info = Some(factory_events_info);
254            }
255
256            e
257        };
258
259        let (listener, data) = unsafe {
260            let factory = &self.factory.proxy.as_ptr();
261
262            let data = Box::into_raw(Box::new(self.cbs));
263            let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
264            let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
265
266            spa_interface_call_method!(
267                factory,
268                pw_sys::pw_factory_methods,
269                add_listener,
270                listener_ptr.cast(),
271                e.as_ref().get_ref(),
272                data as *mut _
273            );
274
275            (listener, Box::from_raw(data))
276        };
277
278        FactoryListener {
279            events: e,
280            listener,
281            data,
282        }
283    }
284}