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