1use bitflags::bitflags;
9use libc::c_void;
10use std::ops::Deref;
11use std::{fmt, mem};
12use std::{pin::Pin, ptr};
13
14use crate::{
15 proxy::{Listener, Proxy, ProxyT},
16 types::ObjectType,
17};
18use spa::{pod::Pod, spa_interface_call_method};
19
20#[derive(Debug)]
22pub struct Device {
23 proxy: Proxy,
24}
25
26impl Device {
27 #[must_use = "Use the builder to register event callbacks"]
29 pub fn add_listener_local(&self) -> DeviceListenerLocalBuilder<'_> {
30 DeviceListenerLocalBuilder {
31 device: self,
32 cbs: ListenerLocalCallbacks::default(),
33 }
34 }
35
36 pub fn subscribe_params(&self, ids: &[spa::param::ParamType]) {
44 unsafe {
45 spa_interface_call_method!(
46 self.proxy.as_ptr(),
47 pw_sys::pw_device_methods,
48 subscribe_params,
49 ids.as_ptr() as *mut _,
50 ids.len().try_into().unwrap()
51 );
52 }
53 }
54
55 pub fn enum_params(&self, seq: i32, id: Option<spa::param::ParamType>, start: u32, num: u32) {
71 let id = id.map(|id| id.as_raw()).unwrap_or(crate::constants::ID_ANY);
72
73 unsafe {
74 spa_interface_call_method!(
75 self.proxy.as_ptr(),
76 pw_sys::pw_device_methods,
77 enum_params,
78 seq,
79 id,
80 start,
81 num,
82 std::ptr::null()
83 );
84 }
85 }
86
87 pub fn set_param(&self, id: spa::param::ParamType, flags: u32, param: &Pod) {
93 unsafe {
94 spa_interface_call_method!(
95 self.proxy.as_ptr(),
96 pw_sys::pw_device_methods,
97 set_param,
98 id.as_raw(),
99 flags,
100 param.as_raw_ptr()
101 );
102 }
103 }
104}
105
106impl ProxyT for Device {
107 fn type_() -> ObjectType {
108 ObjectType::Device
109 }
110
111 fn upcast(self) -> Proxy {
112 self.proxy
113 }
114
115 fn upcast_ref(&self) -> &Proxy {
116 &self.proxy
117 }
118
119 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
120 where
121 Self: Sized,
122 {
123 Self { proxy }
124 }
125}
126
127#[derive(Default)]
128struct ListenerLocalCallbacks {
129 #[allow(clippy::type_complexity)]
130 info: Option<Box<dyn Fn(&DeviceInfoRef)>>,
131 #[allow(clippy::type_complexity)]
132 param: Option<Box<dyn Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>)>>,
133}
134
135pub struct DeviceListenerLocalBuilder<'a> {
155 device: &'a Device,
156 cbs: ListenerLocalCallbacks,
157}
158
159#[repr(transparent)]
160pub struct DeviceInfoRef(pw_sys::pw_device_info);
161
162impl DeviceInfoRef {
163 pub fn as_raw(&self) -> &pw_sys::pw_device_info {
164 &self.0
165 }
166
167 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_device_info {
168 std::ptr::addr_of!(self.0).cast_mut()
169 }
170
171 pub fn id(&self) -> u32 {
172 self.0.id
173 }
174
175 pub fn change_mask(&self) -> DeviceChangeMask {
176 DeviceChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
177 }
178 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
179 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
180 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
181 }
182
183 pub fn params(&self) -> &[spa::param::ParamInfo] {
185 let params = self.0.params;
186 if params.is_null() {
187 &[]
188 } else {
189 unsafe {
190 std::slice::from_raw_parts(params as *const _, self.0.n_params.try_into().unwrap())
191 }
192 }
193 }
194}
195
196impl fmt::Debug for DeviceInfoRef {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 f.debug_struct("DeviceInfoRef")
199 .field("id", &self.id())
200 .field("change-mask", &self.change_mask())
201 .field("props", &self.props())
202 .field("params", &self.params())
203 .finish()
204 }
205}
206
207pub struct DeviceInfo {
208 ptr: ptr::NonNull<pw_sys::pw_device_info>,
209}
210
211impl DeviceInfo {
212 pub fn new(ptr: ptr::NonNull<pw_sys::pw_device_info>) -> Self {
213 Self { ptr }
214 }
215
216 pub fn from_raw(raw: *mut pw_sys::pw_device_info) -> Self {
217 Self {
218 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
219 }
220 }
221
222 pub fn into_raw(self) -> *mut pw_sys::pw_device_info {
223 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
224 }
225}
226
227impl Drop for DeviceInfo {
228 fn drop(&mut self) {
229 unsafe { pw_sys::pw_device_info_free(self.ptr.as_ptr()) }
230 }
231}
232
233impl std::ops::Deref for DeviceInfo {
234 type Target = DeviceInfoRef;
235
236 fn deref(&self) -> &Self::Target {
237 unsafe { self.ptr.cast::<DeviceInfoRef>().as_ref() }
238 }
239}
240
241impl AsRef<DeviceInfoRef> for DeviceInfo {
242 fn as_ref(&self) -> &DeviceInfoRef {
243 self.deref()
244 }
245}
246
247bitflags! {
248 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
249 pub struct DeviceChangeMask: u64 {
250 const PROPS = pw_sys::PW_DEVICE_CHANGE_MASK_PROPS as u64;
251 const PARAMS = pw_sys::PW_DEVICE_CHANGE_MASK_PARAMS as u64;
252 }
253}
254
255impl fmt::Debug for DeviceInfo {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 f.debug_struct("DeviceInfo")
258 .field("id", &self.id())
259 .field("change-mask", &self.change_mask())
260 .field("props", &self.props())
261 .field("params", &self.params())
262 .finish()
263 }
264}
265
266#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
271pub struct DeviceListener {
272 #[allow(dead_code)]
274 events: Pin<Box<pw_sys::pw_device_events>>,
275 listener: Pin<Box<spa_sys::spa_hook>>,
276 #[allow(dead_code)]
277 data: Box<ListenerLocalCallbacks>,
278}
279
280impl Listener for DeviceListener {}
281
282impl Drop for DeviceListener {
283 fn drop(&mut self) {
284 spa::utils::hook::remove(*self.listener);
285 }
286}
287
288impl<'a> DeviceListenerLocalBuilder<'a> {
289 #[must_use = "Call `.register()` to start receiving events"]
304 pub fn info<F>(mut self, info: F) -> Self
305 where
306 F: Fn(&DeviceInfoRef) + 'static,
307 {
308 self.cbs.info = Some(Box::new(info));
309 self
310 }
311
312 #[must_use = "Call `.register()` to start receiving events"]
338 pub fn param<F>(mut self, param: F) -> Self
339 where
340 F: Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>) + 'static,
341 {
342 self.cbs.param = Some(Box::new(param));
343 self
344 }
345
346 pub fn register(self) -> DeviceListener {
348 unsafe extern "C" fn device_events_info(
349 data: *mut c_void,
350 info: *const pw_sys::pw_device_info,
351 ) {
352 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
353 let info =
354 ptr::NonNull::new(info as *mut pw_sys::pw_device_info).expect("info is NULL");
355 let info = info.cast::<DeviceInfoRef>().as_ref();
356 callbacks.info.as_ref().unwrap()(info);
357 }
358
359 unsafe extern "C" fn device_events_param(
360 data: *mut c_void,
361 seq: i32,
362 id: u32,
363 index: u32,
364 next: u32,
365 param: *const spa_sys::spa_pod,
366 ) {
367 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
368
369 let id = spa::param::ParamType::from_raw(id);
370 let param = if !param.is_null() {
371 unsafe { Some(Pod::from_raw(param)) }
372 } else {
373 None
374 };
375
376 callbacks.param.as_ref().unwrap()(seq, id, index, next, param);
377 }
378
379 let e = unsafe {
380 let mut e: Pin<Box<pw_sys::pw_device_events>> = Box::pin(mem::zeroed());
381 e.version = pw_sys::PW_VERSION_DEVICE_EVENTS;
382
383 if self.cbs.info.is_some() {
384 e.info = Some(device_events_info);
385 }
386 if self.cbs.param.is_some() {
387 e.param = Some(device_events_param);
388 }
389
390 e
391 };
392
393 let (listener, data) = unsafe {
394 let device = &self.device.proxy.as_ptr();
395
396 let data = Box::into_raw(Box::new(self.cbs));
397 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
398 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
399
400 spa_interface_call_method!(
401 device,
402 pw_sys::pw_device_methods,
403 add_listener,
404 listener_ptr.cast(),
405 e.as_ref().get_ref(),
406 data as *mut _
407 );
408
409 (listener, Box::from_raw(data))
410 };
411
412 DeviceListener {
413 events: e,
414 listener,
415 data,
416 }
417 }
418}