1use libc::{c_char, c_void};
5use std::fmt;
6use std::mem;
7use std::pin::Pin;
8use std::{ffi::CStr, ptr};
9
10use crate::{types::ObjectType, Error};
11
12pub struct Proxy {
13 ptr: ptr::NonNull<pw_sys::pw_proxy>,
14}
15
16impl Proxy {
18 pub(crate) fn new(ptr: ptr::NonNull<pw_sys::pw_proxy>) -> Self {
19 Proxy { ptr }
20 }
21
22 pub(crate) fn as_ptr(&self) -> *mut pw_sys::pw_proxy {
23 self.ptr.as_ptr()
24 }
25
26 pub fn add_listener_local(&self) -> ProxyListenerLocalBuilder<'_> {
27 ProxyListenerLocalBuilder {
28 proxy: self,
29 cbs: ListenerLocalCallbacks::default(),
30 }
31 }
32
33 pub fn id(&self) -> u32 {
34 unsafe { pw_sys::pw_proxy_get_id(self.as_ptr()) }
35 }
36
37 pub fn get_type(&self) -> (ObjectType, u32) {
39 unsafe {
40 let mut version = 0;
41 let proxy_type = pw_sys::pw_proxy_get_type(self.as_ptr(), &mut version);
42 let proxy_type = CStr::from_ptr(proxy_type);
43
44 (
45 ObjectType::from_str(proxy_type.to_str().expect("invalid proxy type")),
46 version,
47 )
48 }
49 }
50
51 pub(crate) fn downcast<P: ProxyT>(self) -> Result<P, (Self, Error)> {
56 if P::type_() == self.get_type().0 {
58 unsafe { Ok(P::from_proxy_unchecked(self)) }
59 } else {
60 Err((self, Error::WrongProxyType))
61 }
62 }
63}
64
65impl Drop for Proxy {
66 fn drop(&mut self) {
67 unsafe {
68 pw_sys::pw_proxy_destroy(self.as_ptr());
69 }
70 }
71}
72
73impl fmt::Debug for Proxy {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 let (proxy_type, version) = self.get_type();
76
77 f.debug_struct("Proxy")
78 .field("id", &self.id())
79 .field("type", &proxy_type)
80 .field("version", &version)
81 .finish()
82 }
83}
84
85pub trait ProxyT {
87 fn type_() -> ObjectType
90 where
91 Self: Sized;
92
93 fn upcast(self) -> Proxy;
94 fn upcast_ref(&self) -> &Proxy;
95
96 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
105 where
106 Self: Sized;
107}
108
109pub trait Listener {}
111
112pub struct ProxyListener {
113 #[allow(dead_code)]
115 events: Pin<Box<pw_sys::pw_proxy_events>>,
116 listener: Pin<Box<spa_sys::spa_hook>>,
117 #[allow(dead_code)]
118 data: Box<ListenerLocalCallbacks>,
119}
120
121impl Listener for ProxyListener {}
122
123impl Drop for ProxyListener {
124 fn drop(&mut self) {
125 spa::utils::hook::remove(*self.listener);
126 }
127}
128#[derive(Default)]
129struct ListenerLocalCallbacks {
130 destroy: Option<Box<dyn Fn()>>,
131 bound: Option<Box<dyn Fn(u32)>>,
132 removed: Option<Box<dyn Fn()>>,
133 done: Option<Box<dyn Fn(i32)>>,
134 #[allow(clippy::type_complexity)]
135 error: Option<Box<dyn Fn(i32, i32, &str)>>, }
137
138pub struct ProxyListenerLocalBuilder<'a> {
139 proxy: &'a Proxy,
140 cbs: ListenerLocalCallbacks,
141}
142
143impl<'a> ProxyListenerLocalBuilder<'a> {
144 #[must_use]
145 pub fn destroy<F>(mut self, destroy: F) -> Self
146 where
147 F: Fn() + 'static,
148 {
149 self.cbs.destroy = Some(Box::new(destroy));
150 self
151 }
152
153 #[must_use]
154 pub fn bound<F>(mut self, bound: F) -> Self
155 where
156 F: Fn(u32) + 'static,
157 {
158 self.cbs.bound = Some(Box::new(bound));
159 self
160 }
161
162 #[must_use]
163 pub fn removed<F>(mut self, removed: F) -> Self
164 where
165 F: Fn() + 'static,
166 {
167 self.cbs.removed = Some(Box::new(removed));
168 self
169 }
170
171 #[must_use]
172 pub fn done<F>(mut self, done: F) -> Self
173 where
174 F: Fn(i32) + 'static,
175 {
176 self.cbs.done = Some(Box::new(done));
177 self
178 }
179
180 #[must_use]
181 pub fn error<F>(mut self, error: F) -> Self
182 where
183 F: Fn(i32, i32, &str) + 'static,
184 {
185 self.cbs.error = Some(Box::new(error));
186 self
187 }
188
189 #[must_use]
190 pub fn register(self) -> ProxyListener {
191 unsafe extern "C" fn proxy_destroy(data: *mut c_void) {
192 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
193 callbacks.destroy.as_ref().unwrap()();
194 }
195
196 unsafe extern "C" fn proxy_bound(data: *mut c_void, global_id: u32) {
197 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
198 callbacks.bound.as_ref().unwrap()(global_id);
199 }
200
201 unsafe extern "C" fn proxy_removed(data: *mut c_void) {
202 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
203 callbacks.removed.as_ref().unwrap()();
204 }
205
206 unsafe extern "C" fn proxy_done(data: *mut c_void, seq: i32) {
207 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
208 callbacks.done.as_ref().unwrap()(seq);
209 }
210
211 unsafe extern "C" fn proxy_error(
212 data: *mut c_void,
213 seq: i32,
214 res: i32,
215 message: *const c_char,
216 ) {
217 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
218 let message = CStr::from_ptr(message).to_str().unwrap();
219 callbacks.error.as_ref().unwrap()(seq, res, message);
220 }
221
222 let e = unsafe {
223 let mut e: Pin<Box<pw_sys::pw_proxy_events>> = Box::pin(mem::zeroed());
224 e.version = pw_sys::PW_VERSION_PROXY_EVENTS;
225
226 if self.cbs.destroy.is_some() {
227 e.destroy = Some(proxy_destroy);
228 }
229
230 if self.cbs.bound.is_some() {
231 e.bound = Some(proxy_bound);
232 }
233
234 if self.cbs.removed.is_some() {
235 e.removed = Some(proxy_removed);
236 }
237
238 if self.cbs.done.is_some() {
239 e.done = Some(proxy_done);
240 }
241
242 if self.cbs.error.is_some() {
243 e.error = Some(proxy_error);
244 }
245
246 e
247 };
248
249 let (listener, data) = unsafe {
250 let proxy = &self.proxy.as_ptr();
251
252 let data = Box::into_raw(Box::new(self.cbs));
253 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
254 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
255 let funcs: *const pw_sys::pw_proxy_events = e.as_ref().get_ref();
256
257 pw_sys::pw_proxy_add_listener(
258 proxy.cast(),
259 listener_ptr.cast(),
260 funcs.cast(),
261 data as *mut _,
262 );
263
264 (listener, Box::from_raw(data))
265 };
266
267 ProxyListener {
268 events: e,
269 listener,
270 data,
271 }
272 }
273}