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