1use bitflags::bitflags;
5use libc::c_void;
6use std::ops::Deref;
7use std::pin::Pin;
8use std::{ffi::CStr, ptr};
9use std::{fmt, mem};
10
11use crate::{
12 proxy::{Listener, Proxy, ProxyT},
13 types::ObjectType,
14};
15use spa::{pod::Pod, spa_interface_call_method};
16
17#[derive(Debug)]
18pub struct Node {
19 proxy: Proxy,
20}
21
22impl Node {
23 #[must_use]
25 pub fn add_listener_local(&self) -> NodeListenerLocalBuilder<'_> {
26 NodeListenerLocalBuilder {
27 node: 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_node_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 pub fn set_param(&self, id: spa::param::ParamType, flags: u32, param: &Pod) {
78 unsafe {
79 spa_interface_call_method!(
80 self.proxy.as_ptr(),
81 pw_sys::pw_node_methods,
82 set_param,
83 id.as_raw(),
84 flags,
85 param.as_raw_ptr()
86 );
87 }
88 }
89}
90
91impl ProxyT for Node {
92 fn type_() -> ObjectType {
93 ObjectType::Node
94 }
95
96 fn upcast(self) -> Proxy {
97 self.proxy
98 }
99
100 fn upcast_ref(&self) -> &Proxy {
101 &self.proxy
102 }
103
104 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
105 where
106 Self: Sized,
107 {
108 Self { proxy }
109 }
110}
111
112#[derive(Default)]
113struct ListenerLocalCallbacks {
114 #[allow(clippy::type_complexity)]
115 info: Option<Box<dyn Fn(&NodeInfoRef)>>,
116 #[allow(clippy::type_complexity)]
117 param: Option<Box<dyn Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>)>>,
118}
119
120pub struct NodeListenerLocalBuilder<'a> {
121 node: &'a Node,
122 cbs: ListenerLocalCallbacks,
123}
124
125#[repr(transparent)]
126pub struct NodeInfoRef(pw_sys::pw_node_info);
127
128impl NodeInfoRef {
129 pub fn as_raw(&self) -> &pw_sys::pw_node_info {
130 &self.0
131 }
132
133 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_node_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 max_input_ports(&self) -> u32 {
142 self.0.max_input_ports
143 }
144
145 pub fn max_output_ports(&self) -> u32 {
146 self.0.max_output_ports
147 }
148
149 pub fn change_mask(&self) -> NodeChangeMask {
150 NodeChangeMask::from_bits_retain(self.0.change_mask)
151 }
152
153 pub fn n_input_ports(&self) -> u32 {
154 self.0.n_input_ports
155 }
156
157 pub fn n_output_ports(&self) -> u32 {
158 self.0.n_output_ports
159 }
160
161 pub fn state(&self) -> NodeState<'_> {
162 let raw_state = self.0.state;
163 match raw_state {
164 pw_sys::pw_node_state_PW_NODE_STATE_ERROR => {
165 let error = unsafe {
166 let error = self.0.error;
167 CStr::from_ptr(error).to_str().unwrap()
168 };
169 NodeState::Error(error)
170 }
171 pw_sys::pw_node_state_PW_NODE_STATE_CREATING => NodeState::Creating,
172 pw_sys::pw_node_state_PW_NODE_STATE_SUSPENDED => NodeState::Suspended,
173 pw_sys::pw_node_state_PW_NODE_STATE_IDLE => NodeState::Idle,
174 pw_sys::pw_node_state_PW_NODE_STATE_RUNNING => NodeState::Running,
175 _ => panic!("Invalid node state: {raw_state}"),
176 }
177 }
178
179 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
180 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
181 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
182 }
183
184 pub fn params(&self) -> &[spa::param::ParamInfo] {
186 unsafe {
187 let params_ptr = self.0.params;
188
189 if params_ptr.is_null() {
190 &[]
191 } else {
192 std::slice::from_raw_parts(
193 params_ptr as *const _,
194 self.0.n_params.try_into().unwrap(),
195 )
196 }
197 }
198 }
199}
200
201impl fmt::Debug for NodeInfoRef {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 f.debug_struct("NodeInfoRef")
204 .field("id", &self.id())
205 .field("max-input-ports", &self.max_input_ports())
206 .field("max-output-ports", &self.max_output_ports())
207 .field("change-mask", &self.change_mask())
208 .field("n-input-ports", &self.n_input_ports())
209 .field("n-output-ports", &self.n_output_ports())
210 .field("state", &self.state())
211 .field("props", &self.props())
212 .field("params", &self.params())
213 .finish()
214 }
215}
216
217pub struct NodeInfo {
218 ptr: ptr::NonNull<pw_sys::pw_node_info>,
219}
220
221impl NodeInfo {
222 pub fn new(ptr: ptr::NonNull<pw_sys::pw_node_info>) -> Self {
223 Self { ptr }
224 }
225
226 pub fn from_raw(raw: *mut pw_sys::pw_node_info) -> Self {
231 Self {
232 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
233 }
234 }
235
236 pub fn into_raw(self) -> *mut pw_sys::pw_node_info {
237 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
238 }
239}
240
241impl Drop for NodeInfo {
242 fn drop(&mut self) {
243 unsafe { pw_sys::pw_node_info_free(self.ptr.as_ptr()) }
244 }
245}
246
247impl std::ops::Deref for NodeInfo {
248 type Target = NodeInfoRef;
249
250 fn deref(&self) -> &Self::Target {
251 unsafe { self.ptr.cast::<NodeInfoRef>().as_ref() }
252 }
253}
254
255impl AsRef<NodeInfoRef> for NodeInfo {
256 fn as_ref(&self) -> &NodeInfoRef {
257 self.deref()
258 }
259}
260
261bitflags! {
262 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
263 pub struct NodeChangeMask: u64 {
264 const INPUT_PORTS = pw_sys::PW_NODE_CHANGE_MASK_INPUT_PORTS as u64;
265 const OUTPUT_PORTS = pw_sys::PW_NODE_CHANGE_MASK_OUTPUT_PORTS as u64;
266 const STATE = pw_sys::PW_NODE_CHANGE_MASK_STATE as u64;
267 const PROPS = pw_sys::PW_NODE_CHANGE_MASK_PROPS as u64;
268 const PARAMS = pw_sys::PW_NODE_CHANGE_MASK_PARAMS as u64;
269 }
270}
271
272#[derive(Debug)]
273pub enum NodeState<'a> {
274 Error(&'a str),
275 Creating,
276 Suspended,
277 Idle,
278 Running,
279}
280
281impl fmt::Debug for NodeInfo {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 f.debug_struct("NodeInfo")
284 .field("id", &self.id())
285 .field("max-input-ports", &self.max_input_ports())
286 .field("max-output-ports", &self.max_output_ports())
287 .field("change-mask", &self.change_mask())
288 .field("n-input-ports", &self.n_input_ports())
289 .field("n-output-ports", &self.n_output_ports())
290 .field("state", &self.state())
291 .field("props", &self.props())
292 .field("params", &self.params())
293 .finish()
294 }
295}
296
297pub struct NodeListener {
298 #[allow(dead_code)]
300 events: Pin<Box<pw_sys::pw_node_events>>,
301 listener: Pin<Box<spa_sys::spa_hook>>,
302 #[allow(dead_code)]
303 data: Box<ListenerLocalCallbacks>,
304}
305
306impl Listener for NodeListener {}
307
308impl Drop for NodeListener {
309 fn drop(&mut self) {
310 spa::utils::hook::remove(*self.listener);
311 }
312}
313
314impl<'a> NodeListenerLocalBuilder<'a> {
315 #[must_use]
316 pub fn info<F>(mut self, info: F) -> Self
317 where
318 F: Fn(&NodeInfoRef) + 'static,
319 {
320 self.cbs.info = Some(Box::new(info));
321 self
322 }
323
324 #[must_use]
325 pub fn param<F>(mut self, param: F) -> Self
326 where
327 F: Fn(i32, spa::param::ParamType, u32, u32, Option<&Pod>) + 'static,
328 {
329 self.cbs.param = Some(Box::new(param));
330 self
331 }
332
333 #[must_use]
334 pub fn register(self) -> NodeListener {
335 unsafe extern "C" fn node_events_info(
336 data: *mut c_void,
337 info: *const pw_sys::pw_node_info,
338 ) {
339 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
340 let info = ptr::NonNull::new(info as *mut pw_sys::pw_node_info).expect("info is NULL");
341 let info = info.cast::<NodeInfoRef>().as_ref();
342 callbacks.info.as_ref().unwrap()(info);
343 }
344
345 unsafe extern "C" fn node_events_param(
346 data: *mut c_void,
347 seq: i32,
348 id: u32,
349 index: u32,
350 next: u32,
351 param: *const spa_sys::spa_pod,
352 ) {
353 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
354
355 let id = spa::param::ParamType::from_raw(id);
356 let param = if !param.is_null() {
357 unsafe { Some(Pod::from_raw(param)) }
358 } else {
359 None
360 };
361
362 callbacks.param.as_ref().unwrap()(seq, id, index, next, param);
363 }
364
365 let e = unsafe {
366 let mut e: Pin<Box<pw_sys::pw_node_events>> = Box::pin(mem::zeroed());
367 e.version = pw_sys::PW_VERSION_NODE_EVENTS;
368
369 if self.cbs.info.is_some() {
370 e.info = Some(node_events_info);
371 }
372 if self.cbs.param.is_some() {
373 e.param = Some(node_events_param);
374 }
375
376 e
377 };
378
379 let (listener, data) = unsafe {
380 let node = &self.node.proxy.as_ptr();
381
382 let data = Box::into_raw(Box::new(self.cbs));
383 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
384 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
385
386 spa_interface_call_method!(
387 node,
388 pw_sys::pw_node_methods,
389 add_listener,
390 listener_ptr.cast(),
391 e.as_ref().get_ref(),
392 data as *mut _
393 );
394
395 (listener, Box::from_raw(data))
396 };
397
398 NodeListener {
399 events: e,
400 listener,
401 data,
402 }
403 }
404}