use std::ffi::CString;
use std::os::raw::c_char;
use std::{
ffi::{c_void, CStr},
mem,
pin::Pin,
ptr,
};
use crate::{
proxy::{Listener, Proxy, ProxyT},
types::ObjectType,
};
use spa::spa_interface_call_method;
#[derive(Debug)]
pub struct Metadata {
proxy: Proxy,
}
impl ProxyT for Metadata {
fn type_() -> ObjectType {
ObjectType::Metadata
}
fn upcast(self) -> Proxy {
self.proxy
}
fn upcast_ref(&self) -> &Proxy {
&self.proxy
}
unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
where
Self: Sized,
{
Self { proxy }
}
}
impl Metadata {
pub fn add_listener_local(&self) -> MetadataListenerLocalBuilder {
MetadataListenerLocalBuilder {
metadata: self,
cbs: ListenerLocalCallbacks::default(),
}
}
pub fn set_property(&self, subject: u32, key: &str, type_: Option<&str>, value: Option<&str>) {
let key = CString::new(key).expect("Invalid byte in metadata key");
let type_ = type_.map(|t| CString::new(t).expect("Invalid byte in metadata type"));
let value = value.map(|v| CString::new(v).expect("Invalid byte in metadata value"));
let key_cstr = key.as_c_str();
Metadata::set_property_cstr(self, subject, key_cstr, type_.as_deref(), value.as_deref())
}
pub fn set_property_cstr(
&self,
subject: u32,
key: &CStr,
type_: Option<&CStr>,
value: Option<&CStr>,
) {
unsafe {
spa::spa_interface_call_method!(
self.proxy.as_ptr(),
pw_sys::pw_metadata_methods,
set_property,
subject,
key.as_ptr() as *const _,
type_.map_or_else(ptr::null, CStr::as_ptr) as *const _,
value.map_or_else(ptr::null, CStr::as_ptr) as *const _
);
}
}
pub fn clear(&self) {
unsafe {
spa::spa_interface_call_method!(
self.proxy.as_ptr(),
pw_sys::pw_metadata_methods,
clear,
);
}
}
}
pub struct MetadataListener {
#[allow(dead_code)]
events: Pin<Box<pw_sys::pw_metadata_events>>,
listener: Pin<Box<spa_sys::spa_hook>>,
#[allow(dead_code)]
data: Box<ListenerLocalCallbacks>,
}
impl Listener for MetadataListener {}
impl Drop for MetadataListener {
fn drop(&mut self) {
spa::utils::hook::remove(*self.listener);
}
}
#[derive(Default)]
struct ListenerLocalCallbacks {
#[allow(clippy::type_complexity)]
property: Option<Box<dyn Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32>>,
}
#[must_use]
pub struct MetadataListenerLocalBuilder<'meta> {
metadata: &'meta Metadata,
cbs: ListenerLocalCallbacks,
}
impl<'meta> MetadataListenerLocalBuilder<'meta> {
pub fn property<F>(mut self, property: F) -> Self
where
F: Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32 + 'static,
{
self.cbs.property = Some(Box::new(property));
self
}
#[must_use]
pub fn register(self) -> MetadataListener {
unsafe extern "C" fn metadata_events_property(
data: *mut c_void,
subject: u32,
key: *const c_char,
type_: *const c_char,
value: *const c_char,
) -> i32 {
let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
let key = if !key.is_null() {
Some(CStr::from_ptr(key).to_string_lossy())
} else {
None
};
let type_ = if !type_.is_null() {
Some(CStr::from_ptr(type_).to_string_lossy())
} else {
None
};
let value = if !value.is_null() {
Some(CStr::from_ptr(value).to_string_lossy())
} else {
None
};
callbacks.property.as_ref().unwrap()(
subject,
key.as_deref(),
type_.as_deref(),
value.as_deref(),
)
}
let e = unsafe {
let mut e: Pin<Box<pw_sys::pw_metadata_events>> = Box::pin(mem::zeroed());
e.version = pw_sys::PW_VERSION_METADATA_EVENTS;
if self.cbs.property.is_some() {
e.property = Some(metadata_events_property);
}
e
};
let (listener, data) = unsafe {
let metadata = &self.metadata.proxy.as_ptr();
let data = Box::into_raw(Box::new(self.cbs));
let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
spa_interface_call_method!(
metadata,
pw_sys::pw_metadata_methods,
add_listener,
listener_ptr.cast(),
e.as_ref().get_ref(),
data as *mut _
);
(listener, Box::from_raw(data))
};
MetadataListener {
events: e,
listener,
data,
}
}
}