use std::{convert::TryInto, os::unix::prelude::*, ptr, time::Duration};
use libc::{c_int, c_void};
pub use rustix::process::Signal;
use spa::{spa_interface_call_method, support::system::IoFlags, utils::result::SpaResult};
use crate::utils::assert_main_thread;
mod box_;
pub use box_::*;
mod rc;
pub use rc::*;
#[repr(transparent)]
pub struct Loop(pw_sys::pw_loop);
impl Loop {
pub fn as_raw(&self) -> &pw_sys::pw_loop {
&self.0
}
pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_loop {
std::ptr::addr_of!(self.0).cast_mut()
}
pub fn fd(&self) -> BorrowedFd<'_> {
unsafe {
let mut iface = self.as_raw().control.as_ref().unwrap().iface;
let raw_fd = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_control_methods,
get_fd,
);
BorrowedFd::borrow_raw(raw_fd)
}
}
pub unsafe fn enter(&self) {
let mut iface = self.as_raw().control.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_control_methods,
enter,
)
}
pub unsafe fn leave(&self) {
let mut iface = self.as_raw().control.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_control_methods,
leave,
)
}
pub fn iterate(&self, timeout: Timeout) -> i32 {
struct LeaveGuard<'a>(&'a Loop);
impl Drop for LeaveGuard<'_> {
fn drop(&mut self) {
unsafe {
self.0.leave();
}
}
}
unsafe {
self.enter();
let _guard = LeaveGuard(self);
self.iterate_unguarded(timeout)
}
}
pub unsafe fn iterate_unguarded(&self, timeout: Timeout) -> i32 {
let mut iface = self.as_raw().control.as_ref().unwrap().iface;
let timeout: c_int =
c_int::try_from(timeout).expect("Provided timeout does not fit in a c_int");
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_control_methods,
iterate,
timeout
)
}
#[must_use]
pub fn add_io<I, F>(&self, io: I, event_mask: IoFlags, callback: F) -> IoSource<'_, I>
where
I: AsRawFd,
F: Fn(&mut I) + 'static,
Self: Sized,
{
unsafe extern "C" fn call_closure<I>(data: *mut c_void, _fd: RawFd, _mask: u32)
where
I: AsRawFd,
{
let (io, callback) = (data as *mut IoSourceData<I>).as_mut().unwrap();
callback(io);
}
let fd = io.as_raw_fd();
let data = Box::into_raw(Box::new((io, Box::new(callback) as Box<dyn Fn(&mut I)>)));
let (source, data) = unsafe {
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
let source = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
add_io,
fd,
event_mask.bits(),
false,
Some(call_closure::<I>),
data as *mut _
);
(source, Box::from_raw(data))
};
let ptr = ptr::NonNull::new(source).expect("source is NULL");
IoSource {
ptr,
loop_: self,
_data: data,
}
}
#[must_use]
pub fn add_idle<F>(&self, enabled: bool, callback: F) -> IdleSource<'_>
where
F: Fn() + 'static,
{
unsafe extern "C" fn call_closure<F>(data: *mut c_void)
where
F: Fn(),
{
let callback = (data as *mut F).as_ref().unwrap();
callback();
}
let data = Box::into_raw(Box::new(callback));
let (source, data) = unsafe {
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
let source = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
add_idle,
enabled,
Some(call_closure::<F>),
data as *mut _
);
(source, Box::from_raw(data))
};
let ptr = ptr::NonNull::new(source).expect("source is NULL");
IdleSource {
ptr,
loop_: self,
_data: data,
}
}
#[must_use]
pub fn add_signal_local<F>(&self, signal: Signal, callback: F) -> SignalSource<'_>
where
F: Fn() + 'static,
Self: Sized,
{
assert_main_thread();
unsafe extern "C" fn call_closure<F>(data: *mut c_void, _signal: c_int)
where
F: Fn(),
{
let callback = (data as *mut F).as_ref().unwrap();
callback();
}
let data = Box::into_raw(Box::new(callback));
let (source, data) = unsafe {
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
let source = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
add_signal,
signal.as_raw(),
Some(call_closure::<F>),
data as *mut _
);
(source, Box::from_raw(data))
};
let ptr = ptr::NonNull::new(source).expect("source is NULL");
SignalSource {
ptr,
loop_: self,
_data: data,
}
}
#[must_use]
pub fn add_event<F>(&self, callback: F) -> EventSource<'_>
where
F: Fn() + 'static,
Self: Sized,
{
unsafe extern "C" fn call_closure<F>(data: *mut c_void, _count: u64)
where
F: Fn(),
{
let callback = (data as *mut F).as_ref().unwrap();
callback();
}
let data = Box::into_raw(Box::new(callback));
let (source, data) = unsafe {
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
let source = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
add_event,
Some(call_closure::<F>),
data as *mut _
);
(source, Box::from_raw(data))
};
let ptr = ptr::NonNull::new(source).expect("source is NULL");
EventSource {
ptr,
loop_: self,
_data: data,
}
}
#[must_use]
pub fn add_timer<F>(&self, callback: F) -> TimerSource<'_>
where
F: Fn(u64) + 'static,
Self: Sized,
{
unsafe extern "C" fn call_closure<F>(data: *mut c_void, expirations: u64)
where
F: Fn(u64),
{
let callback = (data as *mut F).as_ref().unwrap();
callback(expirations);
}
let data = Box::into_raw(Box::new(callback));
let (source, data) = unsafe {
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
let source = spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
add_timer,
Some(call_closure::<F>),
data as *mut _
);
(source, Box::from_raw(data))
};
let ptr = ptr::NonNull::new(source).expect("source is NULL");
TimerSource {
ptr,
loop_: self,
_data: data,
}
}
unsafe fn destroy_source<S>(&self, source: &S)
where
S: IsSource,
Self: Sized,
{
let mut iface = self.as_raw().utils.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
destroy_source,
source.as_ptr()
)
}
}
#[derive(Debug, Clone)]
pub enum Timeout {
None,
Infinite,
Finite(Duration),
}
impl TryFrom<Timeout> for c_int {
type Error = <u128 as TryInto<c_int>>::Error;
fn try_from(value: Timeout) -> Result<Self, Self::Error> {
match value {
Timeout::None => Ok(0),
Timeout::Infinite => Ok(-1),
Timeout::Finite(duration) => duration.as_millis().try_into(),
}
}
}
pub trait IsSource {
fn as_ptr(&self) -> *mut spa_sys::spa_source;
}
type IoSourceData<I> = (I, Box<dyn Fn(&mut I) + 'static>);
pub struct IoSource<'l, I>
where
I: AsRawFd,
{
ptr: ptr::NonNull<spa_sys::spa_source>,
loop_: &'l Loop,
_data: Box<IoSourceData<I>>,
}
impl<'l, I> IsSource for IoSource<'l, I>
where
I: AsRawFd,
{
fn as_ptr(&self) -> *mut spa_sys::spa_source {
self.ptr.as_ptr()
}
}
impl<'l, I> Drop for IoSource<'l, I>
where
I: AsRawFd,
{
fn drop(&mut self) {
unsafe { self.loop_.destroy_source(self) }
}
}
pub struct IdleSource<'l> {
ptr: ptr::NonNull<spa_sys::spa_source>,
loop_: &'l Loop,
_data: Box<dyn Fn() + 'static>,
}
impl<'l> IdleSource<'l> {
pub fn enable(&self, enable: bool) {
unsafe {
let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
enable_idle,
self.as_ptr(),
enable
);
}
}
}
impl<'l> IsSource for IdleSource<'l> {
fn as_ptr(&self) -> *mut spa_sys::spa_source {
self.ptr.as_ptr()
}
}
impl<'l> Drop for IdleSource<'l> {
fn drop(&mut self) {
unsafe { self.loop_.destroy_source(self) }
}
}
pub struct SignalSource<'l> {
ptr: ptr::NonNull<spa_sys::spa_source>,
loop_: &'l Loop,
_data: Box<dyn Fn() + 'static>,
}
impl<'l> IsSource for SignalSource<'l> {
fn as_ptr(&self) -> *mut spa_sys::spa_source {
self.ptr.as_ptr()
}
}
impl<'l> Drop for SignalSource<'l> {
fn drop(&mut self) {
unsafe { self.loop_.destroy_source(self) }
}
}
pub struct EventSource<'l> {
ptr: ptr::NonNull<spa_sys::spa_source>,
loop_: &'l Loop,
_data: Box<dyn Fn() + 'static>,
}
impl<'l> IsSource for EventSource<'l> {
fn as_ptr(&self) -> *mut spa_sys::spa_source {
self.ptr.as_ptr()
}
}
impl<'l> EventSource<'l> {
pub fn signal(&self) -> SpaResult {
let res = unsafe {
let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
signal_event,
self.as_ptr()
)
};
SpaResult::from_c(res)
}
}
impl<'l> Drop for EventSource<'l> {
fn drop(&mut self) {
unsafe { self.loop_.destroy_source(self) }
}
}
pub struct TimerSource<'l> {
ptr: ptr::NonNull<spa_sys::spa_source>,
loop_: &'l Loop,
_data: Box<dyn Fn(u64) + 'static>,
}
impl<'l> TimerSource<'l> {
pub fn update_timer(&self, value: Option<Duration>, interval: Option<Duration>) -> SpaResult {
fn duration_to_timespec(duration: Duration) -> spa_sys::timespec {
let mut timespec =
unsafe { std::mem::MaybeUninit::<spa_sys::timespec>::zeroed().assume_init() };
timespec.tv_sec = duration.as_secs().try_into().expect("Duration too long");
#[allow(clippy::unnecessary_fallible_conversions)] {
timespec.tv_nsec = duration
.subsec_nanos()
.try_into()
.expect("Nanoseconds should fit into timespec");
}
timespec
}
let value = duration_to_timespec(value.unwrap_or_default());
let interval = duration_to_timespec(interval.unwrap_or_default());
let res = unsafe {
let mut iface = self.loop_.as_raw().utils.as_ref().unwrap().iface;
spa_interface_call_method!(
&mut iface as *mut spa_sys::spa_interface,
spa_sys::spa_loop_utils_methods,
update_timer,
self.as_ptr(),
&value as *const _ as *mut _,
&interval as *const _ as *mut _,
false
)
};
SpaResult::from_c(res)
}
}
impl<'l> IsSource for TimerSource<'l> {
fn as_ptr(&self) -> *mut spa_sys::spa_source {
self.ptr.as_ptr()
}
}
impl<'l> Drop for TimerSource<'l> {
fn drop(&mut self) {
unsafe { self.loop_.destroy_source(self) }
}
}