1use std::{
2 ffi::{c_void, CStr},
3 fmt, mem,
4 ops::Deref,
5 pin::Pin,
6 ptr,
7};
8
9use bitflags::bitflags;
10use spa::spa_interface_call_method;
11
12use crate::{
13 proxy::{Listener, Proxy, ProxyT},
14 types::ObjectType,
15};
16
17#[derive(Debug)]
18pub struct Link {
19 proxy: Proxy,
20}
21
22impl ProxyT for Link {
23 fn type_() -> ObjectType {
24 ObjectType::Link
25 }
26
27 fn upcast(self) -> Proxy {
28 self.proxy
29 }
30
31 fn upcast_ref(&self) -> &Proxy {
32 &self.proxy
33 }
34
35 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
36 where
37 Self: Sized,
38 {
39 Self { proxy }
40 }
41}
42
43impl Link {
44 #[must_use]
45 pub fn add_listener_local(&self) -> LinkListenerLocalBuilder<'_> {
46 LinkListenerLocalBuilder {
47 link: self,
48 cbs: ListenerLocalCallbacks::default(),
49 }
50 }
51}
52
53pub struct LinkListener {
54 #[allow(dead_code)]
56 events: Pin<Box<pw_sys::pw_link_events>>,
57 listener: Pin<Box<spa_sys::spa_hook>>,
58 #[allow(dead_code)]
59 data: Box<ListenerLocalCallbacks>,
60}
61
62impl Listener for LinkListener {}
63
64impl Drop for LinkListener {
65 fn drop(&mut self) {
66 spa::utils::hook::remove(*self.listener);
67 }
68}
69
70#[derive(Default)]
71struct ListenerLocalCallbacks {
72 #[allow(clippy::type_complexity)]
73 info: Option<Box<dyn Fn(&LinkInfoRef)>>,
74}
75
76pub struct LinkListenerLocalBuilder<'link> {
77 link: &'link Link,
78 cbs: ListenerLocalCallbacks,
79}
80
81impl<'a> LinkListenerLocalBuilder<'a> {
82 #[must_use]
83 pub fn info<F>(mut self, info: F) -> Self
84 where
85 F: Fn(&LinkInfoRef) + 'static,
86 {
87 self.cbs.info = Some(Box::new(info));
88 self
89 }
90
91 #[must_use]
92 pub fn register(self) -> LinkListener {
93 unsafe extern "C" fn link_events_info(
94 data: *mut c_void,
95 info: *const pw_sys::pw_link_info,
96 ) {
97 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
98 let info = ptr::NonNull::new(info as *mut pw_sys::pw_link_info).expect("info is NULL");
99 let info = info.cast::<LinkInfoRef>().as_ref();
100 callbacks.info.as_ref().unwrap()(info);
101 }
102
103 let e = unsafe {
104 let mut e: Pin<Box<pw_sys::pw_link_events>> = Box::pin(mem::zeroed());
105 e.version = pw_sys::PW_VERSION_LINK_EVENTS;
106
107 if self.cbs.info.is_some() {
108 e.info = Some(link_events_info);
109 }
110
111 e
112 };
113
114 let (listener, data) = unsafe {
115 let link = &self.link.proxy.as_ptr();
116
117 let data = Box::into_raw(Box::new(self.cbs));
118 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
119 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
120
121 spa_interface_call_method!(
122 link,
123 pw_sys::pw_link_methods,
124 add_listener,
125 listener_ptr.cast(),
126 e.as_ref().get_ref(),
127 data as *mut _
128 );
129
130 (listener, Box::from_raw(data))
131 };
132
133 LinkListener {
134 events: e,
135 listener,
136 data,
137 }
138 }
139}
140
141#[repr(transparent)]
142pub struct LinkInfoRef(pw_sys::pw_link_info);
143
144impl LinkInfoRef {
145 pub fn as_raw(&self) -> &pw_sys::pw_link_info {
146 &self.0
147 }
148
149 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_link_info {
150 std::ptr::addr_of!(self.0).cast_mut()
151 }
152
153 pub fn id(&self) -> u32 {
154 self.0.id
155 }
156
157 pub fn output_node_id(&self) -> u32 {
158 self.0.output_node_id
159 }
160
161 pub fn output_port_id(&self) -> u32 {
162 self.0.output_port_id
163 }
164
165 pub fn input_node_id(&self) -> u32 {
166 self.0.input_node_id
167 }
168
169 pub fn input_port_id(&self) -> u32 {
170 self.0.input_port_id
171 }
172
173 pub fn state(&self) -> LinkState<'_> {
174 let raw_state = self.0.state;
175 match raw_state {
176 pw_sys::pw_link_state_PW_LINK_STATE_ERROR => {
177 let error = unsafe { CStr::from_ptr(self.0.error).to_str().unwrap() };
178 LinkState::Error(error)
179 }
180 pw_sys::pw_link_state_PW_LINK_STATE_UNLINKED => LinkState::Unlinked,
181 pw_sys::pw_link_state_PW_LINK_STATE_INIT => LinkState::Init,
182 pw_sys::pw_link_state_PW_LINK_STATE_NEGOTIATING => LinkState::Negotiating,
183 pw_sys::pw_link_state_PW_LINK_STATE_ALLOCATING => LinkState::Allocating,
184 pw_sys::pw_link_state_PW_LINK_STATE_PAUSED => LinkState::Paused,
185 pw_sys::pw_link_state_PW_LINK_STATE_ACTIVE => LinkState::Active,
186 _ => panic!("Invalid link state: {raw_state}"),
187 }
188 }
189
190 pub fn change_mask(&self) -> LinkChangeMask {
191 LinkChangeMask::from_bits_retain(self.0.change_mask)
192 }
193
194 pub fn format(&self) -> Option<&spa::pod::Pod> {
195 let format = self.0.format;
196 if format.is_null() {
197 None
198 } else {
199 Some(unsafe { spa::pod::Pod::from_raw(format) })
200 }
201 }
202
203 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
204 let props_ptr: *mut spa::utils::dict::DictRef = self.0.props.cast();
205 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
206 }
207}
208
209impl fmt::Debug for LinkInfoRef {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 f.debug_struct("LinkInfoRef")
212 .field("id", &self.id())
213 .field("output_node_id", &self.output_node_id())
214 .field("output_port_id", &self.output_port_id())
215 .field("input_node_id", &self.input_node_id())
216 .field("input_port_id", &self.input_port_id())
217 .field("change-mask", &self.change_mask())
218 .field("state", &self.state())
219 .field("props", &self.props())
220 .finish()
222 }
223}
224
225pub struct LinkInfo {
226 ptr: ptr::NonNull<pw_sys::pw_link_info>,
227}
228
229impl LinkInfo {
230 pub fn new(ptr: ptr::NonNull<pw_sys::pw_link_info>) -> Self {
231 Self { ptr }
232 }
233
234 pub fn from_raw(raw: *mut pw_sys::pw_link_info) -> Self {
235 Self {
236 ptr: ptr::NonNull::new(raw).expect("Provided pointer is null"),
237 }
238 }
239
240 pub fn into_raw(self) -> *mut pw_sys::pw_link_info {
241 std::mem::ManuallyDrop::new(self).ptr.as_ptr()
242 }
243}
244
245impl Drop for LinkInfo {
246 fn drop(&mut self) {
247 unsafe { pw_sys::pw_link_info_free(self.ptr.as_ptr()) }
248 }
249}
250
251impl std::ops::Deref for LinkInfo {
252 type Target = LinkInfoRef;
253
254 fn deref(&self) -> &Self::Target {
255 unsafe { self.ptr.cast::<LinkInfoRef>().as_ref() }
256 }
257}
258
259impl AsRef<LinkInfoRef> for LinkInfo {
260 fn as_ref(&self) -> &LinkInfoRef {
261 self.deref()
262 }
263}
264
265bitflags! {
266 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
267 pub struct LinkChangeMask: u64 {
268 const STATE = pw_sys::PW_LINK_CHANGE_MASK_STATE as u64;
269 const FORMAT = pw_sys::PW_LINK_CHANGE_MASK_FORMAT as u64;
270 const PROPS = pw_sys::PW_LINK_CHANGE_MASK_PROPS as u64;
271 }
272}
273
274impl fmt::Debug for LinkInfo {
275 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276 f.debug_struct("LinkInfo")
277 .field("id", &self.id())
278 .field("output_node_id", &self.output_node_id())
279 .field("output_port_id", &self.output_port_id())
280 .field("input_node_id", &self.input_node_id())
281 .field("input_port_id", &self.input_port_id())
282 .field("change-mask", &self.change_mask())
283 .field("state", &self.state())
284 .field("props", &self.props())
285 .finish()
287 }
288}
289
290#[derive(Debug)]
291pub enum LinkState<'a> {
292 Error(&'a str),
293 Unlinked,
294 Init,
295 Negotiating,
296 Allocating,
297 Paused,
298 Active,
299}