1use std::any::Any;
6use std::sync::{Arc, Mutex, RwLock};
7
8use pipewire_native_spa as spa;
9
10use crate::HookId;
11use crate::{new_refcounted, properties::Properties, refcounted, Refcounted};
12
13use crate::{types::ObjectType, Id};
14
15pub mod client;
17pub mod device;
19pub mod factory;
21pub mod link;
23pub mod metadata;
25pub mod module;
27pub mod node;
29pub mod port;
31pub mod profiler;
33pub mod registry;
35
36refcounted! {
37 pub struct Proxy<T: HasProxy + Refcounted> {
68 object: T::WeakRef,
69 id: Id,
70 bound_id: RwLock<Option<Id>>,
71 hooks: Arc<Mutex<spa::hook::HookList<ProxyEvents>>>,
72 }
73}
74
75#[allow(clippy::type_complexity)]
77#[derive(Default)]
78pub struct ProxyEvents {
79 pub destroy: Option<Box<dyn FnMut() + Send>>,
82 pub bound: Option<Box<dyn FnMut(Id) + Send>>,
84 pub removed: Option<Box<dyn FnMut() + Send>>,
86 pub done: Option<Box<dyn FnMut(u32) + Send>>,
88 pub error: Option<Box<dyn FnMut(u32, u32, &str) + Send>>,
90 pub bound_props: Option<Box<dyn FnMut(u32, &Properties) + Send>>,
92}
93
94impl<T: HasProxy + Refcounted> Proxy<T> {
95 pub(crate) fn new(id: Id, object: &T) -> Self {
96 Self {
97 inner: new_refcounted(InnerProxy::<T>::new(id, object.downgrade())),
98 }
99 }
100
101 pub fn id(&self) -> Id {
103 self.inner.id
104 }
105
106 pub fn object(&self) -> Option<T> {
109 Refcounted::upgrade(&self.inner.object)
110 }
111
112 pub fn bound_id(&self) -> Option<Id> {
114 *self.inner.bound_id.read().unwrap()
115 }
116
117 pub(crate) fn set_bound_id(&self, id: Id) {
118 *self.inner.bound_id.write().unwrap() = Some(id);
119 spa::emit_hook!(self.inner.hooks, bound, id);
120 }
121
122 pub(crate) fn set_bound_props(&self, id: Id, props: &Properties) {
123 *self.inner.bound_id.write().unwrap() = Some(id);
124 spa::emit_hook!(self.inner.hooks, bound_props, id, props);
125 }
126
127 pub fn add_listener(&self, events: ProxyEvents) -> HookId {
129 self.inner.hooks.lock().unwrap().append(events)
130 }
131
132 pub fn remove_listener(&self, hook_id: HookId) {
134 self.inner.hooks.lock().unwrap().remove(hook_id);
135 }
136
137 pub(crate) fn events(&self) -> Arc<Mutex<spa::hook::HookList<ProxyEvents>>> {
138 self.inner.hooks.clone()
139 }
140}
141
142impl<T: HasProxy + Refcounted> InnerProxy<T> {
143 fn new(id: Id, object: T::WeakRef) -> Self {
144 Self {
145 object,
146 id,
147 bound_id: RwLock::new(None),
148 hooks: spa::hook::HookList::new(),
149 }
150 }
151}
152
153pub trait HasProxy: Any + Send + Sync {
156 fn type_(&self) -> ObjectType;
162
163 fn version(&self) -> u32;
165
166 fn proxy(&self) -> Proxy<Self>
168 where
169 Self: Refcounted;
170}
171
172impl dyn HasProxy {
173 pub fn downcast<T: HasProxy + Refcounted>(&self) -> Option<T> {
175 (self as &dyn Any).downcast_ref::<T>().cloned()
176 }
177
178 pub fn downcast_proxy<T: HasProxy + Refcounted>(&self) -> Option<Proxy<T>> {
180 (self as &dyn Any).downcast_ref::<T>().map(|o| o.proxy())
181 }
182}
183
184#[doc(hidden)]
194#[macro_export]
195macro_rules! proxy_object_invoke {
196 ($proxy:ident, $method:ident $(, $($args:tt)*)?) => {
197 ($proxy.object().unwrap().methods().lock().unwrap().$method)(&$proxy $(, $($args)*)?)
198 };
199}
200
201#[doc(hidden)]
202#[macro_export]
203macro_rules! proxy_object_notify {
204 ($proxy:ident, $event:ident $(, $($args:tt)*)?) => {
205 if let Some(_object) = $proxy.object() {
206 spa::emit_hook!(_object.events(), $event $(, $($args)*)?);
207 }
208 };
209}
210
211#[doc(hidden)]
214#[macro_export]
215macro_rules! hasproxy_method_call_internal {
216 ($object:expr, $unlock:block, $method:ident $(, $($args:tt),*)?) => {
217 {
218 if $object.type_() == $crate::types::interface::CORE {
219 let _proxy = $object.downcast_proxy::<$crate::core::Core>().unwrap();
220 $unlock
221 _proxy.$method($($($args),*)?)
222 } else if $object.type_() == $crate::types::interface::CLIENT {
223 let _proxy = $object.downcast_proxy::<$crate::proxy::client::Client>().unwrap();
224 $unlock
225 _proxy.$method($($($args),*)?)
226 } else if $object.type_() == $crate::types::interface::DEVICE {
227 let _proxy = $object.downcast_proxy::<$crate::proxy::device::Device>().unwrap();
228 $unlock
229 _proxy.$method($($($args),*)?)
230 } else if $object.type_() == $crate::types::interface::FACTORY {
231 let _proxy = $object.downcast_proxy::<$crate::proxy::factory::Factory>().unwrap();
232 $unlock
233 _proxy.$method($($($args),*)?)
234 } else if $object.type_() == $crate::types::interface::LINK {
235 let _proxy = $object.downcast_proxy::<$crate::proxy::link::Link>().unwrap();
236 $unlock
237 _proxy.$method($($($args),*)?)
238 } else if $object.type_() == $crate::types::interface::METADATA {
239 let _proxy = $object.downcast_proxy::<$crate::proxy::metadata::Metadata>().unwrap();
240 $unlock
241 _proxy.$method($($($args),*)?)
242 } else if $object.type_() == $crate::types::interface::MODULE {
243 let _proxy = $object.downcast_proxy::<$crate::proxy::module::Module>().unwrap();
244 $unlock
245 _proxy.$method($($($args),*)?)
246 } else if $object.type_() == $crate::types::interface::NODE {
247 let _proxy = $object.downcast_proxy::<$crate::proxy::node::Node>().unwrap();
248 $unlock
249 _proxy.$method($($($args),*)?)
250 } else if $object.type_() == $crate::types::interface::PORT {
251 let _proxy = $object.downcast_proxy::<$crate::proxy::port::Port>().unwrap();
252 $unlock
253 _proxy.$method($($($args),*)?)
254 } else if $object.type_() == $crate::types::interface::PROFILER {
255 let _proxy = $object.downcast_proxy::<$crate::proxy::profiler::Profiler>().unwrap();
256 $unlock
257 _proxy.$method($($($args),*)?)
258 } else if $object.type_() == $crate::types::interface::REGISTRY {
259 let _proxy = $object.downcast_proxy::<$crate::proxy::registry::Registry>().unwrap();
260 $unlock
261 _proxy.$method($($($args),*)?)
262 } else {
263 unreachable!("got unexpected proxy type {}", $object.type_())
264 }
265 }
266 };
267}
268
269#[doc(hidden)]
270#[macro_export]
271macro_rules! hasproxy_method_call {
272 ($object:expr, $method:ident $(, $($args:tt),*)?) => {
273 $crate::hasproxy_method_call_internal!($object, {}, $method $(, $($args),*)?)
274 };
275}
276
277#[doc(hidden)]
278#[macro_export]
279macro_rules! hasproxy_method_call_unlocked {
280 ($object:expr, $lock: ident, $method:ident $(, $($args:tt),*)?) => {
281 $crate::hasproxy_method_call_internal!($object, { drop($lock); }, $method $(, $($args),*)?)
282 };
283}
284
285#[doc(hidden)]
286#[macro_export]
287macro_rules! hasproxy_notify_internal {
288 ($object:ident, $unlock:block, $event:ident $(, $($args:tt),*)?) => {
289 if $object.type_() == $crate::types::interface::CORE {
290 let _proxy = $object.downcast_proxy::<$crate::core::Core>().unwrap();
291 $unlock
292 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
293 } else if $object.type_() == $crate::types::interface::CLIENT {
294 let _proxy = $object.downcast_proxy::<$crate::proxy::client::Client>().unwrap();
295 $unlock
296 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
297 } else if $object.type_() == $crate::types::interface::DEVICE {
298 let _proxy = $object.downcast_proxy::<$crate::proxy::device::Device>().unwrap();
299 $unlock
300 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
301 } else if $object.type_() == $crate::types::interface::FACTORY {
302 let _proxy = $object.downcast_proxy::<$crate::proxy::factory::Factory>().unwrap();
303 $unlock
304 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
305 } else if $object.type_() == $crate::types::interface::LINK {
306 let _proxy = $object.downcast_proxy::<$crate::proxy::link::Link>().unwrap();
307 $unlock
308 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
309 } else if $object.type_() == $crate::types::interface::METADATA {
310 let _proxy = $object.downcast_proxy::<$crate::proxy::metadata::Metadata>().unwrap();
311 $unlock
312 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
313 } else if $object.type_() == $crate::types::interface::MODULE {
314 let _proxy = $object.downcast_proxy::<$crate::proxy::module::Module>().unwrap();
315 $unlock
316 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
317 } else if $object.type_() == $crate::types::interface::NODE {
318 let _proxy = $object.downcast_proxy::<$crate::proxy::node::Node>().unwrap();
319 $unlock
320 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
321 } else if $object.type_() == $crate::types::interface::PORT {
322 let _proxy = $object.downcast_proxy::<$crate::proxy::port::Port>().unwrap();
323 $unlock
324 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
325 } else if $object.type_() == $crate::types::interface::PROFILER {
326 let _proxy = $object.downcast_proxy::<$crate::proxy::profiler::Profiler>().unwrap();
327 $unlock
328 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
329 } else if $object.type_() == $crate::types::interface::REGISTRY {
330 let _proxy = $object.downcast_proxy::<$crate::proxy::registry::Registry>().unwrap();
331 $unlock
332 spa::emit_hook!(_proxy.events(), $event $(, $($args),*)?)
333 } else {
334 unreachable!("got unexpected proxy type {}", $object.type_())
335 }
336 };
337}
338
339#[doc(hidden)]
340#[macro_export]
341macro_rules! hasproxy_notify {
342 ($object:ident, $event:ident $(, $($args:tt),*)?) => {
343 $crate::hasproxy_notify_internal!($object, {}, $event $(, $($args),*)?)
344 };
345}
346
347#[doc(hidden)]
348#[macro_export]
349macro_rules! hasproxy_notify_unlocked {
350 ($object:ident, $lock:ident, $event:ident $(, $($args:tt),*)?) => {
351 $crate::hasproxy_notify_internal!($object, { drop($lock); }, $event $(, $($args),*)?)
352 };
353}
354
355#[doc(hidden)]
356#[macro_export]
357macro_rules! proxy_notify {
358 ($object:ident, $event:ident $(, $($args:tt),*)?) => {
359 spa::emit_hook!($object.proxy().events(), $event $(, $($args),*)?)
360 };
361}