1use bitflags::bitflags;
5use libc::c_void;
6use std::ops::Deref;
7use std::{fmt, mem};
8use std::{pin::Pin, ptr};
9
10use crate::{
11 proxy::{Listener, Proxy, ProxyT},
12 types::ObjectType,
13};
14use spa::{pod::Pod, spa_interface_call_method};
15
16#[derive(Debug)]
17pub struct Device {
18 proxy: Proxy,
19}
20
21impl Device {
22 #[must_use]
24 pub fn add_listener_local(&self) -> DeviceListenerLocalBuilder<'_> {
25 DeviceListenerLocalBuilder {
26 device: self,
27 cbs: ListenerLocalCallbacks::default(),
28 }
29 }
30
31 pub fn subscribe_params(&self, ids: &[spa::param::ParamType]) {
36 unsafe {
37 spa_interface_call_method!(
38 self.proxy.as_ptr(),
39 pw_sys::pw_device_methods,
40 subscribe_params,
41 ids.as_ptr() as *mut _,
42 ids.len().try_into().unwrap()
43 );
44 }
45 }
46
47 pub fn enum_params(&self, seq: i32, id: Option<spa::param::ParamType>, start: u32, num: u32) {
60 let id = id.map(|id| id.as_raw()).unwrap_or(crate::constants::ID_ANY);
61
62 unsafe {
63 spa_interface_call_method!(
64 self.proxy.as_ptr(),
65 pw_sys::pw_device_methods,
66 enum_params,
67 seq,
68 id,
69 start,
70 num,
71 std::ptr::null()
72 );
73 }
74 }
75
76 pub fn set_param(&self, id: spa::param::ParamType, flags: u32, param: &Pod) {
77 unsafe {
78 spa_interface_call_method!(
79 self.proxy.as_ptr(),
80 pw_sys::pw_device_methods,
81 set_param,
82 id.as_raw(),
83 flags,
84 param.as_raw_ptr()
85 );
86 }
87 }
88}
89
90impl ProxyT for Device {
91 fn type_() -> ObjectType {
92 ObjectType::Device
93 }
94
95 fn upcast(self) -> Proxy {
96 self.proxy
97 }
98
99 fn upcast_ref(&self) -> &Proxy {
100 &self.proxy
101 }
102
103 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
104 where
105 Self: Sized,
106 {
107 Self { proxy }
108 }
109}
110
111#[derive(Default)]
112struct ListenerLocalCallbacks {
113 #[allow(clippy::type_complexity)]
114 info: Option<Box<dyn Fn(&DeviceInfoRef)>>,
115 #[allow(clippy::type_complexity)]
116 param: Option<Box<dyn Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>)>>,
117}
118
119pub struct DeviceListenerLocalBuilder<'a> {
120 device: &'a Device,
121 cbs: ListenerLocalCallbacks,
122}
123
124#[repr(transparent)]
125pub struct DeviceInfoRef(pw_sys::pw_device_info);
126
127impl DeviceInfoRef {
128 pub fn as_raw(&self) -> &pw_sys::pw_device_info {
129 &self.0
130 }
131
132 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_device_info {
133 std::ptr::addr_of!(self.0).cast_mut()
134 }
135
136 pub fn id(&self) -> u32 {
137 self.0.id
138 }
139
140 pub fn change_mask(&self) -> DeviceChangeMask {
141 DeviceChangeMask::from_bits(self.0.change_mask).expect("invalid change_mask")
142 }
143 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
144 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
145 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
146 }
147
148 pub fn params(&self) -> &[spa::param::ParamInfo] {
150 let params = self.0.params;
151 if params.is_null() {
152 &[]
153 } else {
154 unsafe {
155 std::slice::from_raw_parts(params as *const _, self.0.n_params.try_into().unwrap())
156 }
157 }
158 }
159}
160
161impl fmt::Debug for DeviceInfoRef {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 f.debug_struct("DeviceInfoRef")
164 .field("id", &self.id())
165 .field("change-mask", &self.change_mask())
166 .field("props", &self.props())
167 .field("params", &self.params())
168 .finish()
169 }
170}
171
172pub struct DeviceInfo {
173 ptr: ptr::NonNull<pw_sys::pw_device_info>,
174}
175
176impl DeviceInfo {
177 pub fn new(ptr: ptr::NonNull<pw_sys::pw_device_info>) -> Self {
178 Self { ptr }
179 }
180
181 pub fn from_raw(raw: *mut pw_sys::pw_device_info) -> Self {
182 Self {
183 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
184 }
185 }
186
187 pub fn into_raw(self) -> *mut pw_sys::pw_device_info {
188 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
189 }
190}
191
192impl Drop for DeviceInfo {
193 fn drop(&mut self) {
194 unsafe { pw_sys::pw_device_info_free(self.ptr.as_ptr()) }
195 }
196}
197
198impl std::ops::Deref for DeviceInfo {
199 type Target = DeviceInfoRef;
200
201 fn deref(&self) -> &Self::Target {
202 unsafe { self.ptr.cast::<DeviceInfoRef>().as_ref() }
203 }
204}
205
206impl AsRef<DeviceInfoRef> for DeviceInfo {
207 fn as_ref(&self) -> &DeviceInfoRef {
208 self.deref()
209 }
210}
211
212bitflags! {
213 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
214 pub struct DeviceChangeMask: u64 {
215 const PROPS = pw_sys::PW_DEVICE_CHANGE_MASK_PROPS as u64;
216 const PARAMS = pw_sys::PW_DEVICE_CHANGE_MASK_PARAMS as u64;
217 }
218}
219
220impl fmt::Debug for DeviceInfo {
221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222 f.debug_struct("DeviceInfo")
223 .field("id", &self.id())
224 .field("change-mask", &self.change_mask())
225 .field("props", &self.props())
226 .field("params", &self.params())
227 .finish()
228 }
229}
230
231pub struct DeviceListener {
232 #[allow(dead_code)]
234 events: Pin<Box<pw_sys::pw_device_events>>,
235 listener: Pin<Box<spa_sys::spa_hook>>,
236 #[allow(dead_code)]
237 data: Box<ListenerLocalCallbacks>,
238}
239
240impl Listener for DeviceListener {}
241
242impl Drop for DeviceListener {
243 fn drop(&mut self) {
244 spa::utils::hook::remove(*self.listener);
245 }
246}
247
248impl<'a> DeviceListenerLocalBuilder<'a> {
249 #[must_use]
250 pub fn info<F>(mut self, info: F) -> Self
251 where
252 F: Fn(&DeviceInfoRef) + 'static,
253 {
254 self.cbs.info = Some(Box::new(info));
255 self
256 }
257
258 #[must_use]
259 pub fn param<F>(mut self, param: F) -> Self
260 where
261 F: Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>) + 'static,
262 {
263 self.cbs.param = Some(Box::new(param));
264 self
265 }
266
267 #[must_use]
268 pub fn register(self) -> DeviceListener {
269 unsafe extern "C" fn device_events_info(
270 data: *mut c_void,
271 info: *const pw_sys::pw_device_info,
272 ) {
273 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
274 let info =
275 ptr::NonNull::new(info as *mut pw_sys::pw_device_info).expect("info is NULL");
276 let info = info.cast::<DeviceInfoRef>().as_ref();
277 callbacks.info.as_ref().unwrap()(info);
278 }
279
280 unsafe extern "C" fn device_events_param(
281 data: *mut c_void,
282 seq: i32,
283 id: u32,
284 index: u32,
285 next: u32,
286 param: *const spa_sys::spa_pod,
287 ) {
288 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
289
290 let id = spa::param::ParamType::from_raw(id);
291 let param = if !param.is_null() {
292 unsafe { Some(Pod::from_raw(param)) }
293 } else {
294 None
295 };
296
297 callbacks.param.as_ref().unwrap()(seq, id, index, next, param);
298 }
299
300 let e = unsafe {
301 let mut e: Pin<Box<pw_sys::pw_device_events>> = Box::pin(mem::zeroed());
302 e.version = pw_sys::PW_VERSION_DEVICE_EVENTS;
303
304 if self.cbs.info.is_some() {
305 e.info = Some(device_events_info);
306 }
307 if self.cbs.param.is_some() {
308 e.param = Some(device_events_param);
309 }
310
311 e
312 };
313
314 let (listener, data) = unsafe {
315 let device = &self.device.proxy.as_ptr();
316
317 let data = Box::into_raw(Box::new(self.cbs));
318 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
319 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
320
321 spa_interface_call_method!(
322 device,
323 pw_sys::pw_device_methods,
324 add_listener,
325 listener_ptr.cast(),
326 e.as_ref().get_ref(),
327 data as *mut _
328 );
329
330 (listener, Box::from_raw(data))
331 };
332
333 DeviceListener {
334 events: e,
335 listener,
336 data,
337 }
338 }
339}