pipewire/
proxy.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4//! Proxy are client side representations of resources that live on a remote PipeWire instance.
5//!
6//! This module contains wrappers for [`pw_proxy`](pw_sys::pw_proxy) and related items.
7
8use libc::{c_char, c_void};
9use std::fmt;
10use std::mem;
11use std::pin::Pin;
12use std::{ffi::CStr, ptr};
13
14use crate::{types::ObjectType, Error};
15
16/// A proxy to a remote object.
17///
18/// Acts as a client side proxy to an object existing in a remote pipewire instance.
19/// The proxy is responsible for converting interface functions invoked by the client to PipeWire messages.
20/// Events will call the callbacks registered in listeners.
21pub struct Proxy {
22    ptr: ptr::NonNull<pw_sys::pw_proxy>,
23}
24
25// Wrapper around a proxy pointer
26impl Proxy {
27    pub(crate) fn new(ptr: ptr::NonNull<pw_sys::pw_proxy>) -> Self {
28        Proxy { ptr }
29    }
30
31    pub(crate) fn as_ptr(&self) -> *mut pw_sys::pw_proxy {
32        self.ptr.as_ptr()
33    }
34
35    #[must_use = "Use the builder to register event callbacks"]
36    pub fn add_listener_local(&self) -> ProxyListenerLocalBuilder<'_> {
37        ProxyListenerLocalBuilder {
38            proxy: self,
39            cbs: ListenerLocalCallbacks::default(),
40        }
41    }
42
43    pub fn id(&self) -> u32 {
44        unsafe { pw_sys::pw_proxy_get_id(self.as_ptr()) }
45    }
46
47    /// Get the type of the proxy as well as it's version.
48    pub fn get_type(&self) -> (ObjectType, u32) {
49        unsafe {
50            let mut version = 0;
51            let proxy_type = pw_sys::pw_proxy_get_type(self.as_ptr(), &mut version);
52            let proxy_type = CStr::from_ptr(proxy_type);
53
54            (
55                ObjectType::from_str(proxy_type.to_str().expect("invalid proxy type")),
56                version,
57            )
58        }
59    }
60
61    /// Attempt to downcast the proxy to the provided type.
62    ///
63    /// The downcast will fail if the type that the proxy represents does not match the provided type. \
64    /// In that case, the function returns `(self, Error::WrongProxyType)` so that the proxy is not lost.
65    pub(crate) fn downcast<P: ProxyT>(self) -> Result<P, (Self, Error)> {
66        // Make sure the proxy we got has the type that is requested
67        if P::type_() == self.get_type().0 {
68            unsafe { Ok(P::from_proxy_unchecked(self)) }
69        } else {
70            Err((self, Error::WrongProxyType))
71        }
72    }
73}
74
75impl Drop for Proxy {
76    fn drop(&mut self) {
77        unsafe {
78            pw_sys::pw_proxy_destroy(self.as_ptr());
79        }
80    }
81}
82
83impl fmt::Debug for Proxy {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        let (proxy_type, version) = self.get_type();
86
87        f.debug_struct("Proxy")
88            .field("id", &self.id())
89            .field("type", &proxy_type)
90            .field("version", &version)
91            .finish()
92    }
93}
94
95// Trait implemented by high level proxy wrappers
96pub trait ProxyT {
97    // Add Sized restriction on those methods so it can be used as a
98    // trait object, see E0038
99    fn type_() -> ObjectType
100    where
101        Self: Sized;
102
103    fn upcast(self) -> Proxy;
104    fn upcast_ref(&self) -> &Proxy;
105
106    /// Downcast the provided proxy to `Self` without checking that the type matches.
107    ///
108    /// This function should not be used by applications.
109    /// If you really do need a way to downcast a proxy to it's type, please open an issue.
110    ///
111    /// # Safety
112    /// It must be manually ensured that the provided proxy is actually a proxy representing the created type. \
113    /// Otherwise, undefined behaviour may occur.
114    unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
115    where
116        Self: Sized;
117}
118
119// Trait implemented by listener on high level proxy wrappers.
120pub trait Listener {}
121
122/// An owned listener for proxy events.
123///
124/// This is created by [`ProxyListenerLocalBuilder`] and will receive events as long as it is alive.
125/// When this gets dropped, the listener gets unregistered and no events will be received by it.
126#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
127pub struct ProxyListener {
128    // Need to stay allocated while the listener is registered
129    #[allow(dead_code)]
130    events: Pin<Box<pw_sys::pw_proxy_events>>,
131    listener: Pin<Box<spa_sys::spa_hook>>,
132    #[allow(dead_code)]
133    data: Box<ListenerLocalCallbacks>,
134}
135
136impl Listener for ProxyListener {}
137
138impl Drop for ProxyListener {
139    fn drop(&mut self) {
140        spa::utils::hook::remove(*self.listener);
141    }
142}
143#[derive(Default)]
144struct ListenerLocalCallbacks {
145    destroy: Option<Box<dyn Fn()>>,
146    bound: Option<Box<dyn Fn(u32)>>,
147    removed: Option<Box<dyn Fn()>>,
148    done: Option<Box<dyn Fn(i32)>>,
149    #[allow(clippy::type_complexity)]
150    error: Option<Box<dyn Fn(i32, i32, &str)>>, // TODO: return a proper Error enum?
151}
152
153/// A builder for registering proxy event callbacks.
154///
155/// Use [`Proxy::add_listener_local`] to create this and register callbacks that will be called when events of interest occur.
156/// After adding callbacks, use [`register`](Self::register) to get back a [`ProxyListener`].
157///
158/// # Examples
159/// ```
160/// # use pipewire::proxy::Proxy;
161/// # fn example(proxy: Proxy) {
162/// let proxy_listener = proxy.add_listener_local()
163///     .destroy(|| println!("Proxy has been destroyed"))
164///     .bound(|id| println!("Proxy has been bound to global {id}"))
165///     .removed(|| println!("Proxy has been removed"))
166///     .done(|seq| println!("Proxy received done with seq {seq}"))
167///     .error(|seq, res, message| println!("Proxy error: seq {seq}, error code {res}, message {message}"))
168///     .register();
169/// # }
170/// ```
171pub struct ProxyListenerLocalBuilder<'a> {
172    proxy: &'a Proxy,
173    cbs: ListenerLocalCallbacks,
174}
175
176impl<'a> ProxyListenerLocalBuilder<'a> {
177    /// Set the proxy `destroy` event callback of the listener.
178    ///
179    /// This event is emitted when the proxy is destroyed.
180    ///
181    /// # Examples
182    /// ```
183    /// # use pipewire::proxy::Proxy;
184    /// # fn example(proxy: Proxy) {
185    /// let proxy_listener = proxy.add_listener_local()
186    ///     .destroy(|| println!("Proxy has been destroyed"))
187    ///     .register();
188    /// # }
189    /// ```
190    #[must_use = "Call `.register()` to start receiving events"]
191    pub fn destroy<F>(mut self, destroy: F) -> Self
192    where
193        F: Fn() + 'static,
194    {
195        self.cbs.destroy = Some(Box::new(destroy));
196        self
197    }
198
199    /// Set the proxy `bound` event callback of the listener.
200    ///
201    /// This event is emitted when the proxy is bound to a global id.
202    ///
203    /// # Callback parameters
204    /// `id`: The global id
205    ///
206    /// # Examples
207    /// ```
208    /// # use pipewire::proxy::Proxy;
209    /// # fn example(proxy: Proxy) {
210    /// let proxy_listener = proxy.add_listener_local()
211    ///     .bound(|id| println!("Proxy has been bound to global {id}"))
212    ///     .register();
213    /// # }
214    /// ```
215    #[must_use = "Call `.register()` to start receiving events"]
216    pub fn bound<F>(mut self, bound: F) -> Self
217    where
218        F: Fn(u32) + 'static,
219    {
220        self.cbs.bound = Some(Box::new(bound));
221        self
222    }
223
224    /// Set the proxy `removed` event callback of the listener.
225    ///
226    /// This event is emitted when the proxy is removed from the server.
227    /// Drop the proxy to free it.
228    ///
229    /// # Examples
230    /// ```
231    /// # use pipewire::proxy::Proxy;
232    /// # fn example(proxy: Proxy) {
233    /// let proxy_listener = proxy.add_listener_local()
234    ///     .removed(|| println!("Proxy has been removed"))
235    ///     .register();
236    /// # }
237    /// ```
238    #[must_use = "Call `.register()` to start receiving events"]
239    pub fn removed<F>(mut self, removed: F) -> Self
240    where
241        F: Fn() + 'static,
242    {
243        self.cbs.removed = Some(Box::new(removed));
244        self
245    }
246
247    /// Set the proxy `done` event callback of the listener.
248    ///
249    /// This event is emitted as a reply to the sync method.
250    ///
251    /// # Callback parameters
252    /// `seq`: The sequence number of the sync call.
253    ///
254    /// # Examples
255    /// ```
256    /// # use pipewire::proxy::Proxy;
257    /// # fn example(proxy: Proxy) {
258    /// let proxy_listener = proxy.add_listener_local()
259    ///     .done(|seq| println!("Proxy received done with seq {seq}"))
260    ///     .register();
261    /// # }
262    /// ```
263    #[must_use = "Call `.register()` to start receiving events"]
264    pub fn done<F>(mut self, done: F) -> Self
265    where
266        F: Fn(i32) + 'static,
267    {
268        self.cbs.done = Some(Box::new(done));
269        self
270    }
271
272    /// Set the proxy `error` event callback of the listener.
273    ///
274    /// This event is emitted when an error occurs on the proxy.
275    ///
276    /// # Callback parameters
277    /// `seq`: Seqeunce number that generated the error  
278    /// `res`: Error code  
279    /// `message`: Error description
280    ///
281    /// # Examples
282    /// ```
283    /// # use pipewire::proxy::Proxy;
284    /// # fn example(proxy: Proxy) {
285    /// let proxy_listener = proxy.add_listener_local()
286    ///     .error(|seq, res, message| println!("Proxy error: seq {seq}, error code {res}, message {message}"))
287    ///     .register();
288    /// # }
289    /// ```
290    #[must_use = "Call `.register()` to start receiving events"]
291    pub fn error<F>(mut self, error: F) -> Self
292    where
293        F: Fn(i32, i32, &str) + 'static,
294    {
295        self.cbs.error = Some(Box::new(error));
296        self
297    }
298
299    /// Subscribe to events and register any provided callbacks.
300    pub fn register(self) -> ProxyListener {
301        unsafe extern "C" fn proxy_destroy(data: *mut c_void) {
302            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
303            callbacks.destroy.as_ref().unwrap()();
304        }
305
306        unsafe extern "C" fn proxy_bound(data: *mut c_void, global_id: u32) {
307            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
308            callbacks.bound.as_ref().unwrap()(global_id);
309        }
310
311        unsafe extern "C" fn proxy_removed(data: *mut c_void) {
312            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
313            callbacks.removed.as_ref().unwrap()();
314        }
315
316        unsafe extern "C" fn proxy_done(data: *mut c_void, seq: i32) {
317            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
318            callbacks.done.as_ref().unwrap()(seq);
319        }
320
321        unsafe extern "C" fn proxy_error(
322            data: *mut c_void,
323            seq: i32,
324            res: i32,
325            message: *const c_char,
326        ) {
327            let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
328            let message = CStr::from_ptr(message).to_str().unwrap();
329            callbacks.error.as_ref().unwrap()(seq, res, message);
330        }
331
332        let e = unsafe {
333            let mut e: Pin<Box<pw_sys::pw_proxy_events>> = Box::pin(mem::zeroed());
334            e.version = pw_sys::PW_VERSION_PROXY_EVENTS;
335
336            if self.cbs.destroy.is_some() {
337                e.destroy = Some(proxy_destroy);
338            }
339
340            if self.cbs.bound.is_some() {
341                e.bound = Some(proxy_bound);
342            }
343
344            if self.cbs.removed.is_some() {
345                e.removed = Some(proxy_removed);
346            }
347
348            if self.cbs.done.is_some() {
349                e.done = Some(proxy_done);
350            }
351
352            if self.cbs.error.is_some() {
353                e.error = Some(proxy_error);
354            }
355
356            e
357        };
358
359        let (listener, data) = unsafe {
360            let proxy = &self.proxy.as_ptr();
361
362            let data = Box::into_raw(Box::new(self.cbs));
363            let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
364            let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
365            let funcs: *const pw_sys::pw_proxy_events = e.as_ref().get_ref();
366
367            pw_sys::pw_proxy_add_listener(
368                proxy.cast(),
369                listener_ptr.cast(),
370                funcs.cast(),
371                data as *mut _,
372            );
373
374            (listener, Box::from_raw(data))
375        };
376
377        ProxyListener {
378            events: e,
379            listener,
380            data,
381        }
382    }
383}