pipewire/loop_/
mod.rs

1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4use 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/// A transparent wrapper around a raw [`pw_loop`](`pw_sys::pw_loop`).
18/// It is usually only seen in a reference (`&Loop`), and does not own the `pw_loop`.
19///
20/// Owned versions, [`LoopRc`] for shared ownership and [`LoopBox`] for unique ownership, are available,
21/// which lets you create and own a [`pw_loop`](`pw_sys::pw_loop`).
22///
23/// Other objects, such as [`MainLoop`](`crate::main_loop::MainLoop`), can also contain loops.
24#[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    /// Get the file descriptor backing this loop.
37    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    /// Enter a loop
52    ///
53    /// Start an iteration of the loop. This function should be called
54    /// before calling iterate and is typically used to capture the thread
55    /// that this loop will run in.
56    ///
57    /// # Safety
58    /// Each call of `enter` must be paired with a call of `leave`.
59    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    /// Leave a loop
70    ///
71    /// Ends the iteration of a loop. This should be called after calling
72    /// iterate.
73    ///
74    /// # Safety
75    /// Each call of `leave` must be paired with a call of `enter`.
76    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    /// Perform one iteration of the loop.
87    ///
88    /// Timeout must be provided, see [`Timeout`].
89    ///
90    /// This function will block
91    /// up to the provided timeout and then dispatch the fds with activity.
92    /// The number of dispatched fds is returned.
93    ///
94    /// This will automatically call [`Self::enter()`] on the loop before iterating, and [`Self::leave()`] afterwards.
95    ///
96    /// # Panics
97    /// This function will panic if the provided timeout as milliseconds does not fit inside a
98    /// `c_int` integer.
99    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    /// A variant of [`iterate()`](`Self::iterate()`) that does not call [`Self::enter()`]  and [`Self::leave()`] on the loop.
110    ///
111    /// # Safety
112    /// Before calling this, [`Self::enter()`] must be called, and [`Self::leave()`] must be called afterwards.
113    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    /// Register some type of IO object with a callback that is called when reading/writing on the IO object
128    /// is available.
129    ///
130    /// The specified `event_mask` determines whether to trigger when either input, output, or any of the two is available.
131    ///
132    /// The returned IoSource needs to take ownership of the IO object, but will provide a reference to the callback when called.
133    #[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                // Never let the loop close the fd, this should be handled via `Drop` implementations.
161                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    /// Register a callback to be called whenever the loop is idle.
179    ///
180    /// This can be enabled and disabled as needed with the `enabled` parameter,
181    /// and also with the `enable` method on the returned source.
182    #[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    /// Register a signal with a callback that is called when the signal is sent.
222    ///
223    /// For example, this can be used to quit the loop when the process receives the `SIGTERM` signal.
224    #[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    /// Register a new event with a callback that is called when the event happens.
267    ///
268    /// The returned [`EventSource`] can be used to trigger the event.
269    #[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    /// Register a timer with the loop with a callback that is called after the timer expired.
308    ///
309    /// The timer will start out inactive, and the returned [`TimerSource`] can be used to arm the timer, or disarm it again.
310    ///
311    /// The callback will be provided with the number of timer expirations since the callback was last called.
312    #[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    /// Destroy a source that belongs to this loop.
351    ///
352    /// # Safety
353    /// The provided source must belong to this loop.
354    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/// Timeout for [`Loop::iterate()`]
371#[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    /// # Errors
381    /// This function will return an error if the provided timeout as milliseconds does not fit inside a
382    /// `c_int` integer.
383    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    /// Return a valid pointer to a raw `spa_source`.
394    fn as_ptr(&self) -> *mut spa_sys::spa_source;
395}
396
397type IoSourceData<I> = (I, Box<dyn Fn(&mut I) + 'static>);
398
399/// A source that can be used to react to IO events.
400///
401/// This source can be obtained by calling [`add_io`](`Loop::add_io`) on a loop, registering a callback to it.
402pub struct IoSource<'l, I>
403where
404    I: AsRawFd,
405{
406    ptr: ptr::NonNull<spa_sys::spa_source>,
407    loop_: &'l Loop,
408    // Store data wrapper to prevent leak
409    _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
430/// A source that can be used to have a callback called when the loop is idle.
431///
432/// This source can be obtained by calling [`add_idle`](`Loop::add_idle`) on a loop, registering a callback to it.
433pub struct IdleSource<'l> {
434    ptr: ptr::NonNull<spa_sys::spa_source>,
435    loop_: &'l Loop,
436    // Store data wrapper to prevent leak
437    _data: Box<dyn Fn() + 'static>,
438}
439
440impl<'l> IdleSource<'l> {
441    /// Set the source as enabled or disabled, allowing or preventing the callback from being called.
442    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
469/// A source that can be used to react to signals.
470///
471/// This source can be obtained by calling [`add_signal_local`](`Loop::add_signal_local`) on a loop, registering a callback to it.
472pub struct SignalSource<'l> {
473    ptr: ptr::NonNull<spa_sys::spa_source>,
474    loop_: &'l Loop,
475    // Store data wrapper to prevent leak
476    _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
491/// A source that can be used to signal to a loop that an event has occurred.
492///
493/// This source can be obtained by calling [`add_event`](`Loop::add_event`) on a loop, registering a callback to it.
494///
495/// By calling [`signal`](`EventSource::signal`) on the `EventSource`, the loop is signaled that the event has occurred.
496/// It will then call the callback at the next possible occasion.
497pub struct EventSource<'l> {
498    ptr: ptr::NonNull<spa_sys::spa_source>,
499    loop_: &'l Loop,
500    // Store data wrapper to prevent leak
501    _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    /// Signal the loop associated with this source that the event has occurred,
512    /// to make the loop call the callback at the next possible occasion.
513    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
535/// A source that can be used to have a callback called on a timer.
536///
537/// This source can be obtained by calling [`add_timer`](`Loop::add_timer`) on a loop, registering a callback to it.
538///
539/// The timer starts out inactive.
540/// You can arm or disarm the timer by calling [`update_timer`](`Self::update_timer`).
541pub struct TimerSource<'l> {
542    ptr: ptr::NonNull<spa_sys::spa_source>,
543    loop_: &'l Loop,
544    // Store data wrapper to prevent leak
545    _data: Box<dyn Fn(u64) + 'static>,
546}
547
548impl<'l> TimerSource<'l> {
549    /// Arm or disarm the timer.
550    ///
551    /// The timer will be called the next time after the provided `value` duration.
552    /// After that, the timer will be repeatedly called again at the the specified `interval`.
553    ///
554    /// If `interval` is `None` or zero, the timer will only be called once. \
555    /// If `value` is `None` or zero, the timer will be disabled.
556    ///
557    /// # Panics
558    /// The provided durations seconds must fit in an i64. Otherwise, this function will panic.
559    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                // `Into` is only implemented on some platforms for these types,
564                // so use a fallible conversion.
565                // As there are a limited amount of nanoseconds in a second, this shouldn't fail
566                #[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}