1use std::{convert::TryInto, os::unix::prelude::*, ptr, time::Duration};
5
6use libc::{c_int, c_void};
7pub use nix::sys::signal::Signal;
8use spa::{spa_interface_call_method, support::system::IoFlags, utils::result::SpaResult};
9
10use crate::utils::assert_main_thread;
11
12mod box_;
13pub use box_::*;
14mod rc;
15pub use rc::*;
16
17#[repr(transparent)]
25pub struct Loop(pw_sys::pw_loop);
26
27impl Loop {
28 pub fn as_raw(&self) -> &pw_sys::pw_loop {
29 &self.0
30 }
31
32 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_loop {
33 std::ptr::addr_of!(self.0).cast_mut()
34 }
35
36 pub fn fd(&self) -> BorrowedFd<'_> {
38 unsafe {
39 let mut iface = self.as_raw().control.as_ref().unwrap().iface;
40
41 let raw_fd = spa_interface_call_method!(
42 &mut iface as *mut spa_sys::spa_interface,
43 spa_sys::spa_loop_control_methods,
44 get_fd,
45 );
46
47 BorrowedFd::borrow_raw(raw_fd)
48 }
49 }
50
51 pub unsafe fn enter(&self) {
60 let mut iface = self.as_raw().control.as_ref().unwrap().iface;
61
62 spa_interface_call_method!(
63 &mut iface as *mut spa_sys::spa_interface,
64 spa_sys::spa_loop_control_methods,
65 enter,
66 )
67 }
68
69 pub unsafe fn leave(&self) {
77 let mut iface = self.as_raw().control.as_ref().unwrap().iface;
78
79 spa_interface_call_method!(
80 &mut iface as *mut spa_sys::spa_interface,
81 spa_sys::spa_loop_control_methods,
82 leave,
83 )
84 }
85
86 pub fn iterate(&self, timeout: Timeout) -> i32 {
100 unsafe {
101 self.enter();
102 let res = self.iterate_unguarded(timeout);
103 self.leave();
104
105 res
106 }
107 }
108
109 pub unsafe fn iterate_unguarded(&self, timeout: Timeout) -> i32 {
114 let mut iface = self.as_raw().control.as_ref().unwrap().iface;
115
116 let timeout: c_int =
117 c_int::try_from(timeout).expect("Provided timeout does not fit in a c_int");
118
119 spa_interface_call_method!(
120 &mut iface as *mut spa_sys::spa_interface,
121 spa_sys::spa_loop_control_methods,
122 iterate,
123 timeout
124 )
125 }
126
127 #[must_use]
134 pub fn add_io<I, F>(&self, io: I, event_mask: IoFlags, callback: F) -> IoSource<'_, I>
135 where
136 I: AsRawFd,
137 F: Fn(&mut I) + 'static,
138 Self: Sized,
139 {
140 unsafe extern "C" fn call_closure<I>(data: *mut c_void, _fd: RawFd, _mask: u32)
141 where
142 I: AsRawFd,
143 {
144 let (io, callback) = (data as *mut IoSourceData<I>).as_mut().unwrap();
145 callback(io);
146 }
147
148 let fd = io.as_raw_fd();
149 let data = Box::into_raw(Box::new((io, Box::new(callback) as Box<dyn Fn(&mut I)>)));
150
151 let (source, data) = unsafe {
152 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
153
154 let source = spa_interface_call_method!(
155 &mut iface as *mut spa_sys::spa_interface,
156 spa_sys::spa_loop_utils_methods,
157 add_io,
158 fd,
159 event_mask.bits(),
160 false,
162 Some(call_closure::<I>),
163 data as *mut _
164 );
165
166 (source, Box::from_raw(data))
167 };
168
169 let ptr = ptr::NonNull::new(source).expect("source is NULL");
170
171 IoSource {
172 ptr,
173 loop_: self,
174 _data: data,
175 }
176 }
177
178 #[must_use]
183 pub fn add_idle<F>(&self, enabled: bool, callback: F) -> IdleSource<'_>
184 where
185 F: Fn() + 'static,
186 {
187 unsafe extern "C" fn call_closure<F>(data: *mut c_void)
188 where
189 F: Fn(),
190 {
191 let callback = (data as *mut F).as_ref().unwrap();
192 callback();
193 }
194
195 let data = Box::into_raw(Box::new(callback));
196
197 let (source, data) = unsafe {
198 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
199
200 let source = spa_interface_call_method!(
201 &mut iface as *mut spa_sys::spa_interface,
202 spa_sys::spa_loop_utils_methods,
203 add_idle,
204 enabled,
205 Some(call_closure::<F>),
206 data as *mut _
207 );
208
209 (source, Box::from_raw(data))
210 };
211
212 let ptr = ptr::NonNull::new(source).expect("source is NULL");
213
214 IdleSource {
215 ptr,
216 loop_: self,
217 _data: data,
218 }
219 }
220
221 #[must_use]
225 pub fn add_signal_local<F>(&self, signal: Signal, callback: F) -> SignalSource<'_>
226 where
227 F: Fn() + 'static,
228 Self: Sized,
229 {
230 assert_main_thread();
231
232 unsafe extern "C" fn call_closure<F>(data: *mut c_void, _signal: c_int)
233 where
234 F: Fn(),
235 {
236 let callback = (data as *mut F).as_ref().unwrap();
237 callback();
238 }
239
240 let data = Box::into_raw(Box::new(callback));
241
242 let (source, data) = unsafe {
243 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
244
245 let source = spa_interface_call_method!(
246 &mut iface as *mut spa_sys::spa_interface,
247 spa_sys::spa_loop_utils_methods,
248 add_signal,
249 signal as c_int,
250 Some(call_closure::<F>),
251 data as *mut _
252 );
253
254 (source, Box::from_raw(data))
255 };
256
257 let ptr = ptr::NonNull::new(source).expect("source is NULL");
258
259 SignalSource {
260 ptr,
261 loop_: self,
262 _data: data,
263 }
264 }
265
266 #[must_use]
270 pub fn add_event<F>(&self, callback: F) -> EventSource<'_>
271 where
272 F: Fn() + 'static,
273 Self: Sized,
274 {
275 unsafe extern "C" fn call_closure<F>(data: *mut c_void, _count: u64)
276 where
277 F: Fn(),
278 {
279 let callback = (data as *mut F).as_ref().unwrap();
280 callback();
281 }
282
283 let data = Box::into_raw(Box::new(callback));
284
285 let (source, data) = unsafe {
286 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
287
288 let source = spa_interface_call_method!(
289 &mut iface as *mut spa_sys::spa_interface,
290 spa_sys::spa_loop_utils_methods,
291 add_event,
292 Some(call_closure::<F>),
293 data as *mut _
294 );
295 (source, Box::from_raw(data))
296 };
297
298 let ptr = ptr::NonNull::new(source).expect("source is NULL");
299
300 EventSource {
301 ptr,
302 loop_: self,
303 _data: data,
304 }
305 }
306
307 #[must_use]
313 pub fn add_timer<F>(&self, callback: F) -> TimerSource<'_>
314 where
315 F: Fn(u64) + 'static,
316 Self: Sized,
317 {
318 unsafe extern "C" fn call_closure<F>(data: *mut c_void, expirations: u64)
319 where
320 F: Fn(u64),
321 {
322 let callback = (data as *mut F).as_ref().unwrap();
323 callback(expirations);
324 }
325
326 let data = Box::into_raw(Box::new(callback));
327
328 let (source, data) = unsafe {
329 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
330
331 let source = spa_interface_call_method!(
332 &mut iface as *mut spa_sys::spa_interface,
333 spa_sys::spa_loop_utils_methods,
334 add_timer,
335 Some(call_closure::<F>),
336 data as *mut _
337 );
338 (source, Box::from_raw(data))
339 };
340
341 let ptr = ptr::NonNull::new(source).expect("source is NULL");
342
343 TimerSource {
344 ptr,
345 loop_: self,
346 _data: data,
347 }
348 }
349
350 unsafe fn destroy_source<S>(&self, source: &S)
355 where
356 S: IsSource,
357 Self: Sized,
358 {
359 let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
360
361 spa_interface_call_method!(
362 &mut iface as *mut spa_sys::spa_interface,
363 spa_sys::spa_loop_utils_methods,
364 destroy_source,
365 source.as_ptr()
366 )
367 }
368}
369
370#[derive(Debug, Clone)]
372pub enum Timeout {
373 None,
374 Infinite,
375 Finite(Duration),
376}
377
378impl TryFrom<Timeout> for c_int {
379 type Error = <u128 as TryInto<c_int>>::Error;
380 fn try_from(value: Timeout) -> Result<Self, Self::Error> {
384 match value {
385 Timeout::None => Ok(0),
386 Timeout::Infinite => Ok(-1),
387 Timeout::Finite(duration) => duration.as_millis().try_into(),
388 }
389 }
390}
391
392pub trait IsSource {
393 fn as_ptr(&self) -> *mut spa_sys::spa_source;
395}
396
397type IoSourceData<I> = (I, Box<dyn Fn(&mut I) + 'static>);
398
399pub struct IoSource<'l, I>
403where
404 I: AsRawFd,
405{
406 ptr: ptr::NonNull<spa_sys::spa_source>,
407 loop_: &'l Loop,
408 _data: Box<IoSourceData<I>>,
410}
411
412impl<'l, I> IsSource for IoSource<'l, I>
413where
414 I: AsRawFd,
415{
416 fn as_ptr(&self) -> *mut spa_sys::spa_source {
417 self.ptr.as_ptr()
418 }
419}
420
421impl<'l, I> Drop for IoSource<'l, I>
422where
423 I: AsRawFd,
424{
425 fn drop(&mut self) {
426 unsafe { self.loop_.destroy_source(self) }
427 }
428}
429
430pub struct IdleSource<'l> {
434 ptr: ptr::NonNull<spa_sys::spa_source>,
435 loop_: &'l Loop,
436 _data: Box<dyn Fn() + 'static>,
438}
439
440impl<'l> IdleSource<'l> {
441 pub fn enable(&self, enable: bool) {
443 unsafe {
444 let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
445
446 spa_interface_call_method!(
447 &mut iface as *mut spa_sys::spa_interface,
448 spa_sys::spa_loop_utils_methods,
449 enable_idle,
450 self.as_ptr(),
451 enable
452 );
453 }
454 }
455}
456
457impl<'l> IsSource for IdleSource<'l> {
458 fn as_ptr(&self) -> *mut spa_sys::spa_source {
459 self.ptr.as_ptr()
460 }
461}
462
463impl<'l> Drop for IdleSource<'l> {
464 fn drop(&mut self) {
465 unsafe { self.loop_.destroy_source(self) }
466 }
467}
468
469pub struct SignalSource<'l> {
473 ptr: ptr::NonNull<spa_sys::spa_source>,
474 loop_: &'l Loop,
475 _data: Box<dyn Fn() + 'static>,
477}
478
479impl<'l> IsSource for SignalSource<'l> {
480 fn as_ptr(&self) -> *mut spa_sys::spa_source {
481 self.ptr.as_ptr()
482 }
483}
484
485impl<'l> Drop for SignalSource<'l> {
486 fn drop(&mut self) {
487 unsafe { self.loop_.destroy_source(self) }
488 }
489}
490
491pub struct EventSource<'l> {
498 ptr: ptr::NonNull<spa_sys::spa_source>,
499 loop_: &'l Loop,
500 _data: Box<dyn Fn() + 'static>,
502}
503
504impl<'l> IsSource for EventSource<'l> {
505 fn as_ptr(&self) -> *mut spa_sys::spa_source {
506 self.ptr.as_ptr()
507 }
508}
509
510impl<'l> EventSource<'l> {
511 pub fn signal(&self) -> SpaResult {
514 let res = unsafe {
515 let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
516
517 spa_interface_call_method!(
518 &mut iface as *mut spa_sys::spa_interface,
519 spa_sys::spa_loop_utils_methods,
520 signal_event,
521 self.as_ptr()
522 )
523 };
524
525 SpaResult::from_c(res)
526 }
527}
528
529impl<'l> Drop for EventSource<'l> {
530 fn drop(&mut self) {
531 unsafe { self.loop_.destroy_source(self) }
532 }
533}
534
535pub struct TimerSource<'l> {
542 ptr: ptr::NonNull<spa_sys::spa_source>,
543 loop_: &'l Loop,
544 _data: Box<dyn Fn(u64) + 'static>,
546}
547
548impl<'l> TimerSource<'l> {
549 pub fn update_timer(&self, value: Option<Duration>, interval: Option<Duration>) -> SpaResult {
560 fn duration_to_timespec(duration: Duration) -> spa_sys::timespec {
561 spa_sys::timespec {
562 tv_sec: duration.as_secs().try_into().expect("Duration too long"),
563 #[allow(clippy::unnecessary_fallible_conversions)]
567 tv_nsec: duration
568 .subsec_nanos()
569 .try_into()
570 .expect("Nanoseconds should fit into timespec"),
571 }
572 }
573
574 let value = duration_to_timespec(value.unwrap_or_default());
575 let interval = duration_to_timespec(interval.unwrap_or_default());
576
577 let res = unsafe {
578 let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
579
580 spa_interface_call_method!(
581 &mut iface as *mut spa_sys::spa_interface,
582 spa_sys::spa_loop_utils_methods,
583 update_timer,
584 self.as_ptr(),
585 &value as *const _ as *mut _,
586 &interval as *const _ as *mut _,
587 false
588 )
589 };
590
591 SpaResult::from_c(res)
592 }
593}
594
595impl<'l> IsSource for TimerSource<'l> {
596 fn as_ptr(&self) -> *mut spa_sys::spa_source {
597 self.ptr.as_ptr()
598 }
599}
600
601impl<'l> Drop for TimerSource<'l> {
602 fn drop(&mut self) {
603 unsafe { self.loop_.destroy_source(self) }
604 }
605}