1use bitflags::bitflags;
5use libc::c_void;
6use std::ops::Deref;
7use std::pin::Pin;
8use std::{
9 ffi::{CStr, CString},
10 ptr,
11};
12use std::{fmt, mem};
13
14use crate::{
15 permissions::Permission,
16 proxy::{Listener, Proxy, ProxyT},
17 types::ObjectType,
18};
19use spa::spa_interface_call_method;
20
21#[derive(Debug)]
22pub struct Client {
23 proxy: Proxy,
24}
25
26impl ProxyT for Client {
27 fn type_() -> ObjectType {
28 ObjectType::Client
29 }
30
31 fn upcast(self) -> Proxy {
32 self.proxy
33 }
34
35 fn upcast_ref(&self) -> &Proxy {
36 &self.proxy
37 }
38
39 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
40 where
41 Self: Sized,
42 {
43 Self { proxy }
44 }
45}
46
47impl Client {
48 #[must_use]
50 pub fn add_listener_local(&self) -> ClientListenerLocalBuilder<'_> {
51 ClientListenerLocalBuilder {
52 client: self,
53 cbs: ListenerLocalCallbacks::default(),
54 }
55 }
56
57 pub fn error(&self, id: u32, res: i32, message: &str) {
58 let message = CString::new(message).expect("Null byte in message parameter");
59 let message_cstr = message.as_c_str();
60 Client::error_cstr(self, id, res, message_cstr)
61 }
62
63 pub fn error_cstr(&self, id: u32, res: i32, message: &CStr) {
64 unsafe {
65 spa_interface_call_method!(
66 self.proxy.as_ptr(),
67 pw_sys::pw_client_methods,
68 error,
69 id,
70 res,
71 message.as_ptr() as *const _
72 );
73 };
74 }
75
76 pub fn update_properties(&self, properties: &spa::utils::dict::DictRef) {
77 unsafe {
78 spa_interface_call_method!(
79 self.proxy.as_ptr(),
80 pw_sys::pw_client_methods,
81 update_properties,
82 properties.as_raw_ptr()
83 );
84 }
85 }
86
87 pub fn get_permissions(&self, index: u32, num: u32) {
88 unsafe {
89 spa_interface_call_method!(
90 self.proxy.as_ptr(),
91 pw_sys::pw_client_methods,
92 get_permissions,
93 index,
94 num
95 );
96 }
97 }
98
99 pub fn update_permissions(&self, permissions: &[Permission]) {
100 unsafe {
101 spa_interface_call_method!(
102 self.proxy.as_ptr(),
103 pw_sys::pw_client_methods,
104 update_permissions,
105 permissions.len() as u32,
106 permissions.as_ptr().cast()
107 );
108 }
109 }
110}
111
112#[derive(Default)]
113struct ListenerLocalCallbacks {
114 #[allow(clippy::type_complexity)]
115 info: Option<Box<dyn Fn(&ClientInfoRef)>>,
116 #[allow(clippy::type_complexity)]
117 permissions: Option<Box<dyn Fn(u32, &[Permission])>>,
118}
119
120pub struct ClientListenerLocalBuilder<'a> {
121 client: &'a Client,
122 cbs: ListenerLocalCallbacks,
123}
124
125#[repr(transparent)]
126pub struct ClientInfoRef(pw_sys::pw_client_info);
127
128impl ClientInfoRef {
129 pub fn as_raw(&self) -> &pw_sys::pw_client_info {
130 &self.0
131 }
132
133 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_client_info {
134 std::ptr::addr_of!(self.0).cast_mut()
135 }
136
137 pub fn id(&self) -> u32 {
138 self.0.id
139 }
140
141 pub fn change_mask(&self) -> ClientChangeMask {
142 ClientChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
143 }
144
145 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
146 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
147 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
148 }
149}
150
151impl fmt::Debug for ClientInfoRef {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 f.debug_struct("ClientInfoRef")
154 .field("id", &self.id())
155 .field("change-mask", &self.change_mask())
156 .field("props", &self.props())
157 .finish()
158 }
159}
160
161pub struct ClientInfo {
162 ptr: ptr::NonNull<pw_sys::pw_client_info>,
163}
164
165impl ClientInfo {
166 pub fn new(ptr: ptr::NonNull<pw_sys::pw_client_info>) -> Self {
167 Self { ptr }
168 }
169
170 pub fn from_raw(raw: *mut pw_sys::pw_client_info) -> Self {
171 Self {
172 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
173 }
174 }
175
176 pub fn into_raw(self) -> *mut pw_sys::pw_client_info {
177 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
178 }
179}
180
181impl Drop for ClientInfo {
182 fn drop(&mut self) {
183 unsafe { pw_sys::pw_client_info_free(self.ptr.as_ptr()) }
184 }
185}
186
187impl std::ops::Deref for ClientInfo {
188 type Target = ClientInfoRef;
189
190 fn deref(&self) -> &Self::Target {
191 unsafe { self.ptr.cast::<ClientInfoRef>().as_ref() }
192 }
193}
194
195impl AsRef<ClientInfoRef> for ClientInfo {
196 fn as_ref(&self) -> &ClientInfoRef {
197 self.deref()
198 }
199}
200
201bitflags! {
202 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
203 pub struct ClientChangeMask: u64 {
204 const PROPS = pw_sys::PW_CLIENT_CHANGE_MASK_PROPS as u64;
205 }
206}
207
208impl fmt::Debug for ClientInfo {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 f.debug_struct("ClientInfo")
211 .field("id", &self.id())
212 .field("change-mask", &self.change_mask())
213 .field("props", &self.props())
214 .finish()
215 }
216}
217
218pub struct ClientListener {
219 #[allow(dead_code)]
221 events: Pin<Box<pw_sys::pw_client_events>>,
222 listener: Pin<Box<spa_sys::spa_hook>>,
223 #[allow(dead_code)]
224 data: Box<ListenerLocalCallbacks>,
225}
226
227impl Listener for ClientListener {}
228
229impl Drop for ClientListener {
230 fn drop(&mut self) {
231 spa::utils::hook::remove(*self.listener);
232 }
233}
234
235impl<'a> ClientListenerLocalBuilder<'a> {
236 #[must_use]
237 pub fn info<F>(mut self, info: F) -> Self
238 where
239 F: Fn(&ClientInfoRef) + 'static,
240 {
241 self.cbs.info = Some(Box::new(info));
242 self
243 }
244
245 pub fn permissions<F>(mut self, permissions: F) -> Self
246 where
247 F: Fn(u32, &[Permission]) + 'static,
248 {
249 self.cbs.permissions = Some(Box::new(permissions));
250 self
251 }
252
253 #[must_use]
254 pub fn register(self) -> ClientListener {
255 unsafe extern "C" fn client_events_info(
256 data: *mut c_void,
257 info: *const pw_sys::pw_client_info,
258 ) {
259 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
260 let info =
261 ptr::NonNull::new(info as *mut pw_sys::pw_client_info).expect("info is NULL");
262 let info = info.cast::<ClientInfoRef>().as_ref();
263 callbacks.info.as_ref().unwrap()(info);
264 }
265
266 unsafe extern "C" fn client_events_permissions(
267 data: *mut c_void,
268 index: u32,
269 n_permissions: u32,
270 permissions: *const pw_sys::pw_permission,
271 ) {
272 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
273 let permissions =
274 std::slice::from_raw_parts(permissions.cast(), n_permissions as usize);
275
276 callbacks.permissions.as_ref().unwrap()(index, permissions);
277 }
278
279 let e = unsafe {
280 let mut e: Pin<Box<pw_sys::pw_client_events>> = Box::pin(mem::zeroed());
281 e.version = pw_sys::PW_VERSION_CLIENT_EVENTS;
282
283 if self.cbs.info.is_some() {
284 e.info = Some(client_events_info);
285 }
286 if self.cbs.permissions.is_some() {
287 e.permissions = Some(client_events_permissions);
288 }
289
290 e
291 };
292
293 let (listener, data) = unsafe {
294 let client = &self.client.proxy.as_ptr();
295
296 let data = Box::into_raw(Box::new(self.cbs));
297 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
298 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
299
300 spa_interface_call_method!(
301 client,
302 pw_sys::pw_client_methods,
303 add_listener,
304 listener_ptr.cast(),
305 e.as_ref().get_ref(),
306 data as *mut _
307 );
308
309 (listener, Box::from_raw(data))
310 };
311
312 ClientListener {
313 events: e,
314 listener,
315 data,
316 }
317 }
318}