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}