1use bitflags::bitflags;
9use libc::c_void;
10use std::ops::Deref;
11use std::pin::Pin;
12use std::{
13 ffi::{CStr, CString},
14 ptr,
15};
16use std::{fmt, mem};
17
18use crate::{
19 permissions::Permission,
20 proxy::{Listener, Proxy, ProxyT},
21 types::ObjectType,
22};
23use spa::spa_interface_call_method;
24
25#[derive(Debug)]
27pub struct Client {
28 proxy: Proxy,
29}
30
31impl ProxyT for Client {
32 fn type_() -> ObjectType {
33 ObjectType::Client
34 }
35
36 fn upcast(self) -> Proxy {
37 self.proxy
38 }
39
40 fn upcast_ref(&self) -> &Proxy {
41 &self.proxy
42 }
43
44 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
45 where
46 Self: Sized,
47 {
48 Self { proxy }
49 }
50}
51
52impl Client {
53 #[must_use = "Use the builder to register event callbacks"]
55 pub fn add_listener_local(&self) -> ClientListenerLocalBuilder<'_> {
56 ClientListenerLocalBuilder {
57 client: self,
58 cbs: ListenerLocalCallbacks::default(),
59 }
60 }
61
62 pub fn error(&self, id: u32, res: i32, message: &str) {
73 let message = CString::new(message).expect("Null byte in message parameter");
74 let message_cstr = message.as_c_str();
75 Client::error_cstr(self, id, res, message_cstr)
76 }
77
78 pub fn error_cstr(&self, id: u32, res: i32, message: &CStr) {
89 unsafe {
90 spa_interface_call_method!(
91 self.proxy.as_ptr(),
92 pw_sys::pw_client_methods,
93 error,
94 id,
95 res,
96 message.as_ptr() as *const _
97 );
98 };
99 }
100
101 pub fn update_properties(&self, properties: &spa::utils::dict::DictRef) {
107 unsafe {
108 spa_interface_call_method!(
109 self.proxy.as_ptr(),
110 pw_sys::pw_client_methods,
111 update_properties,
112 properties.as_raw_ptr()
113 );
114 }
115 }
116
117 pub fn get_permissions(&self, index: u32, num: u32) {
129 unsafe {
130 spa_interface_call_method!(
131 self.proxy.as_ptr(),
132 pw_sys::pw_client_methods,
133 get_permissions,
134 index,
135 num
136 );
137 }
138 }
139
140 pub fn update_permissions(&self, permissions: &[Permission]) {
150 unsafe {
151 spa_interface_call_method!(
152 self.proxy.as_ptr(),
153 pw_sys::pw_client_methods,
154 update_permissions,
155 permissions.len() as u32,
156 permissions.as_ptr().cast()
157 );
158 }
159 }
160}
161
162#[derive(Default)]
163struct ListenerLocalCallbacks {
164 #[allow(clippy::type_complexity)]
165 info: Option<Box<dyn Fn(&ClientInfoRef)>>,
166 #[allow(clippy::type_complexity)]
167 permissions: Option<Box<dyn Fn(u32, &[Permission])>>,
168}
169
170pub struct ClientListenerLocalBuilder<'a> {
188 client: &'a Client,
189 cbs: ListenerLocalCallbacks,
190}
191
192#[repr(transparent)]
193pub struct ClientInfoRef(pw_sys::pw_client_info);
194
195impl ClientInfoRef {
196 pub fn as_raw(&self) -> &pw_sys::pw_client_info {
197 &self.0
198 }
199
200 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_client_info {
201 std::ptr::addr_of!(self.0).cast_mut()
202 }
203
204 pub fn id(&self) -> u32 {
205 self.0.id
206 }
207
208 pub fn change_mask(&self) -> ClientChangeMask {
209 ClientChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
210 }
211
212 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
213 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
214 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
215 }
216}
217
218impl fmt::Debug for ClientInfoRef {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 f.debug_struct("ClientInfoRef")
221 .field("id", &self.id())
222 .field("change-mask", &self.change_mask())
223 .field("props", &self.props())
224 .finish()
225 }
226}
227
228pub struct ClientInfo {
229 ptr: ptr::NonNull<pw_sys::pw_client_info>,
230}
231
232impl ClientInfo {
233 pub fn new(ptr: ptr::NonNull<pw_sys::pw_client_info>) -> Self {
234 Self { ptr }
235 }
236
237 pub fn from_raw(raw: *mut pw_sys::pw_client_info) -> Self {
238 Self {
239 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
240 }
241 }
242
243 pub fn into_raw(self) -> *mut pw_sys::pw_client_info {
244 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
245 }
246}
247
248impl Drop for ClientInfo {
249 fn drop(&mut self) {
250 unsafe { pw_sys::pw_client_info_free(self.ptr.as_ptr()) }
251 }
252}
253
254impl std::ops::Deref for ClientInfo {
255 type Target = ClientInfoRef;
256
257 fn deref(&self) -> &Self::Target {
258 unsafe { self.ptr.cast::<ClientInfoRef>().as_ref() }
259 }
260}
261
262impl AsRef<ClientInfoRef> for ClientInfo {
263 fn as_ref(&self) -> &ClientInfoRef {
264 self.deref()
265 }
266}
267
268bitflags! {
269 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
270 pub struct ClientChangeMask: u64 {
271 const PROPS = pw_sys::PW_CLIENT_CHANGE_MASK_PROPS as u64;
272 }
273}
274
275impl fmt::Debug for ClientInfo {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 f.debug_struct("ClientInfo")
278 .field("id", &self.id())
279 .field("change-mask", &self.change_mask())
280 .field("props", &self.props())
281 .finish()
282 }
283}
284
285#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
290pub struct ClientListener {
291 #[allow(dead_code)]
293 events: Pin<Box<pw_sys::pw_client_events>>,
294 listener: Pin<Box<spa_sys::spa_hook>>,
295 #[allow(dead_code)]
296 data: Box<ListenerLocalCallbacks>,
297}
298
299impl Listener for ClientListener {}
300
301impl Drop for ClientListener {
302 fn drop(&mut self) {
303 spa::utils::hook::remove(*self.listener);
304 }
305}
306
307impl<'a> ClientListenerLocalBuilder<'a> {
308 #[must_use = "Call `.register()` to start receiving events"]
322 pub fn info<F>(mut self, info: F) -> Self
323 where
324 F: Fn(&ClientInfoRef) + 'static,
325 {
326 self.cbs.info = Some(Box::new(info));
327 self
328 }
329
330 #[must_use = "Call `.register()` to start receiving events"]
350 pub fn permissions<F>(mut self, permissions: F) -> Self
351 where
352 F: Fn(u32, &[Permission]) + 'static,
353 {
354 self.cbs.permissions = Some(Box::new(permissions));
355 self
356 }
357
358 pub fn register(self) -> ClientListener {
360 unsafe extern "C" fn client_events_info(
361 data: *mut c_void,
362 info: *const pw_sys::pw_client_info,
363 ) {
364 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
365 let info =
366 ptr::NonNull::new(info as *mut pw_sys::pw_client_info).expect("info is NULL");
367 let info = info.cast::<ClientInfoRef>().as_ref();
368 callbacks.info.as_ref().unwrap()(info);
369 }
370
371 unsafe extern "C" fn client_events_permissions(
372 data: *mut c_void,
373 index: u32,
374 n_permissions: u32,
375 permissions: *const pw_sys::pw_permission,
376 ) {
377 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
378 let permissions =
379 std::slice::from_raw_parts(permissions.cast(), n_permissions as usize);
380
381 callbacks.permissions.as_ref().unwrap()(index, permissions);
382 }
383
384 let e = unsafe {
385 let mut e: Pin<Box<pw_sys::pw_client_events>> = Box::pin(mem::zeroed());
386 e.version = pw_sys::PW_VERSION_CLIENT_EVENTS;
387
388 if self.cbs.info.is_some() {
389 e.info = Some(client_events_info);
390 }
391 if self.cbs.permissions.is_some() {
392 e.permissions = Some(client_events_permissions);
393 }
394
395 e
396 };
397
398 let (listener, data) = unsafe {
399 let client = &self.client.proxy.as_ptr();
400
401 let data = Box::into_raw(Box::new(self.cbs));
402 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
403 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
404
405 spa_interface_call_method!(
406 client,
407 pw_sys::pw_client_methods,
408 add_listener,
409 listener_ptr.cast(),
410 e.as_ref().get_ref(),
411 data as *mut _
412 );
413
414 (listener, Box::from_raw(data))
415 };
416
417 ClientListener {
418 events: e,
419 listener,
420 data,
421 }
422 }
423}