use std::any::Any;
use std::sync::{Arc, Mutex, RwLock};
use pipewire_native_spa as spa;
use crate::HookId;
use crate::{new_refcounted, properties::Properties, refcounted, Refcounted};
use crate::{types::ObjectType, Id};
pub mod client;
pub mod device;
pub mod factory;
pub mod link;
pub mod metadata;
pub mod module;
pub mod node;
pub mod port;
pub mod profiler;
pub mod registry;
refcounted! {
pub struct Proxy<T: HasProxy + Refcounted> {
object: T::WeakRef,
id: Id,
bound_id: RwLock<Option<Id>>,
hooks: Arc<Mutex<spa::hook::HookList<ProxyEvents>>>,
}
}
#[allow(clippy::type_complexity)]
#[derive(Default)]
pub struct ProxyEvents {
pub destroy: Option<Box<dyn FnMut() + Send>>,
pub bound: Option<Box<dyn FnMut(Id) + Send>>,
pub removed: Option<Box<dyn FnMut() + Send>>,
pub done: Option<Box<dyn FnMut(u32) + Send>>,
pub error: Option<Box<dyn FnMut(u32, u32, &str) + Send>>,
pub bound_props: Option<Box<dyn FnMut(u32, &Properties) + Send>>,
}
impl<T: HasProxy + Refcounted> Proxy<T> {
pub(crate) fn new(id: Id, object: &T) -> Self {
Self {
inner: new_refcounted(InnerProxy::<T>::new(id, object.downgrade())),
}
}
pub fn id(&self) -> Id {
self.inner.id
}
pub fn object(&self) -> Option<T> {
Refcounted::upgrade(&self.inner.object)
}
pub fn bound_id(&self) -> Option<Id> {
*self.inner.bound_id.read().unwrap()
}
pub(crate) fn set_bound_id(&self, id: Id) {
*self.inner.bound_id.write().unwrap() = Some(id);
spa::emit_hook!(self.inner.hooks, bound, id);
}
pub(crate) fn set_bound_props(&self, id: Id, props: &Properties) {
*self.inner.bound_id.write().unwrap() = Some(id);
spa::emit_hook!(self.inner.hooks, bound_props, id, props);
}
pub fn add_listener(&self, events: ProxyEvents) -> HookId {
self.inner.hooks.lock().unwrap().append(events)
}
pub fn remove_listener(&self, hook_id: HookId) {
self.inner.hooks.lock().unwrap().remove(hook_id);
}
pub(crate) fn events(&self) -> Arc<Mutex<spa::hook::HookList<ProxyEvents>>> {
self.inner.hooks.clone()
}
}
impl<T: HasProxy + Refcounted> InnerProxy<T> {
fn new(id: Id, object: T::WeakRef) -> Self {
Self {
object,
id,
bound_id: RwLock::new(None),
hooks: spa::hook::HookList::new(),
}
}
}
pub trait HasProxy: Any + Send + Sync {
fn type_(&self) -> ObjectType;
fn version(&self) -> u32;
fn proxy(&self) -> Proxy<Self>
where
Self: Refcounted;
}
impl dyn HasProxy {
pub fn downcast<T: HasProxy + Refcounted>(&self) -> Option<T> {
(self as &dyn Any).downcast_ref::<T>().cloned()
}
pub fn downcast_proxy<T: HasProxy + Refcounted>(&self) -> Option<Proxy<T>> {
(self as &dyn Any).downcast_ref::<T>().map(|o| o.proxy())
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! proxy_object_invoke {
($proxy:ident, $method:ident $(, $($args:tt)*)?) => {
($proxy.object().unwrap().methods().lock().unwrap().$method)(&$proxy $(, $($args)*)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! proxy_object_notify {
($proxy:ident, $event:ident $(, $($args:tt)*)?) => {
if let Some(_object) = $proxy.object() {
spa::emit_hook!(_object.events(), $event $(, $($args)*)?);
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_method_call_internal {
($object:expr, $unlock:block, $method:ident $(, $($args:tt),*)?) => {
{
if $object.type_() == $crate::types::interface::CORE {
let _proxy = $object.downcast_proxy::<$crate::core::Core>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::CLIENT {
let _proxy = $object.downcast_proxy::<$crate::proxy::client::Client>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::DEVICE {
let _proxy = $object.downcast_proxy::<$crate::proxy::device::Device>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::FACTORY {
let _proxy = $object.downcast_proxy::<$crate::proxy::factory::Factory>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::LINK {
let _proxy = $object.downcast_proxy::<$crate::proxy::link::Link>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::METADATA {
let _proxy = $object.downcast_proxy::<$crate::proxy::metadata::Metadata>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::MODULE {
let _proxy = $object.downcast_proxy::<$crate::proxy::module::Module>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::NODE {
let _proxy = $object.downcast_proxy::<$crate::proxy::node::Node>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::PORT {
let _proxy = $object.downcast_proxy::<$crate::proxy::port::Port>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::PROFILER {
let _proxy = $object.downcast_proxy::<$crate::proxy::profiler::Profiler>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else if $object.type_() == $crate::types::interface::REGISTRY {
let _proxy = $object.downcast_proxy::<$crate::proxy::registry::Registry>().unwrap();
$unlock
_proxy.$method($($($args),*)?)
} else {
unreachable!("got unexpected proxy type {}", $object.type_())
}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_method_call {
($object:expr, $method:ident $(, $($args:tt),*)?) => {
$crate::hasproxy_method_call_internal!($object, {}, $method $(, $($args),*)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_method_call_unlocked {
($object:expr, $lock: ident, $method:ident $(, $($args:tt),*)?) => {
$crate::hasproxy_method_call_internal!($object, { drop($lock); }, $method $(, $($args),*)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_notify_internal {
($object:ident, $unlock:block, $event:ident $(, $($args:tt),*)?) => {
if $object.type_() == $crate::types::interface::CORE {
let _proxy = $object.downcast_proxy::<$crate::core::Core>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::CLIENT {
let _proxy = $object.downcast_proxy::<$crate::proxy::client::Client>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::DEVICE {
let _proxy = $object.downcast_proxy::<$crate::proxy::device::Device>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::FACTORY {
let _proxy = $object.downcast_proxy::<$crate::proxy::factory::Factory>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::LINK {
let _proxy = $object.downcast_proxy::<$crate::proxy::link::Link>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::METADATA {
let _proxy = $object.downcast_proxy::<$crate::proxy::metadata::Metadata>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::MODULE {
let _proxy = $object.downcast_proxy::<$crate::proxy::module::Module>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::NODE {
let _proxy = $object.downcast_proxy::<$crate::proxy::node::Node>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::PORT {
let _proxy = $object.downcast_proxy::<$crate::proxy::port::Port>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::PROFILER {
let _proxy = $object.downcast_proxy::<$crate::proxy::profiler::Profiler>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else if $object.type_() == $crate::types::interface::REGISTRY {
let _proxy = $object.downcast_proxy::<$crate::proxy::registry::Registry>().unwrap();
$unlock
spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
} else {
unreachable!("got unexpected proxy type {}", $object.type_())
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_notify {
($object:ident, $event:ident $(, $($args:tt),*)?) => {
$crate::hasproxy_notify_internal!($object, {}, $event $(, $($args),*)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! hasproxy_notify_unlocked {
($object:ident, $lock:ident, $event:ident $(, $($args:tt),*)?) => {
$crate::hasproxy_notify_internal!($object, { drop($lock); }, $event $(, $($args),*)?)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! proxy_notify {
($object:ident, $event:ident $(, $($args:tt),*)?) => {
spa::emit_hook!($object.proxy().events(), $event $(, $($args),*)?)
};
}