1use libc::{c_char, c_void};
5
6use std::{
7 ffi::{CStr, CString},
8 mem,
9 pin::Pin,
10 ptr,
11};
12
13use crate::{
14 permissions::PermissionFlags,
15 properties::PropertiesBox,
16 proxy::{Proxy, ProxyT},
17 types::ObjectType,
18 Error,
19};
20
21mod box_;
22pub use box_::*;
23mod rc;
24pub use rc::*;
25
26#[repr(transparent)]
27pub struct Registry(pw_sys::pw_registry);
28
29impl Registry {
30 pub fn as_raw(&self) -> &pw_sys::pw_registry {
31 &self.0
32 }
33
34 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_registry {
35 std::ptr::addr_of!(self.0).cast_mut()
36 }
37
38 #[must_use]
40 pub fn add_listener_local(&self) -> ListenerLocalBuilder<'_> {
41 ListenerLocalBuilder {
42 registry: self,
43 cbs: ListenerLocalCallbacks::default(),
44 }
45 }
46
47 pub fn bind<T: ProxyT, P: AsRef<spa::utils::dict::DictRef>>(
48 &self,
49 object: &GlobalObject<P>,
50 ) -> Result<T, Error> {
51 let proxy = unsafe {
52 let type_ = CString::new(object.type_.to_str()).unwrap();
53 let version = object.type_.client_version();
54
55 let proxy = spa::spa_interface_call_method!(
56 self.as_raw_ptr(),
57 pw_sys::pw_registry_methods,
58 bind,
59 object.id,
60 type_.as_ptr(),
61 version,
62 0
63 );
64
65 proxy
66 };
67
68 let proxy = ptr::NonNull::new(proxy.cast()).ok_or(Error::NoMemory)?;
69
70 Proxy::new(proxy).downcast().map_err(|(_, e)| e)
71 }
72
73 pub fn destroy_global(&self, global_id: u32) -> spa::utils::result::SpaResult {
75 let result = unsafe {
76 spa::spa_interface_call_method!(
77 self.as_raw_ptr(),
78 pw_sys::pw_registry_methods,
79 destroy,
80 global_id
81 )
82 };
83
84 spa::utils::result::SpaResult::from_c(result)
85 }
86}
87
88type GlobalCallback = dyn Fn(&GlobalObject<&spa::utils::dict::DictRef>);
89type GlobalRemoveCallback = dyn Fn(u32);
90
91#[derive(Default)]
92struct ListenerLocalCallbacks {
93 global: Option<Box<GlobalCallback>>,
94 global_remove: Option<Box<GlobalRemoveCallback>>,
95}
96
97pub struct ListenerLocalBuilder<'a> {
98 registry: &'a Registry,
99 cbs: ListenerLocalCallbacks,
100}
101
102pub struct Listener {
103 #[allow(dead_code)]
105 events: Pin<Box<pw_sys::pw_registry_events>>,
106 listener: Pin<Box<spa_sys::spa_hook>>,
107 #[allow(dead_code)]
108 data: Box<ListenerLocalCallbacks>,
109}
110
111impl Drop for Listener {
112 fn drop(&mut self) {
113 spa::utils::hook::remove(*self.listener);
114 }
115}
116
117impl<'a> ListenerLocalBuilder<'a> {
118 #[must_use]
119 pub fn global<F>(mut self, global: F) -> Self
120 where
121 F: Fn(&GlobalObject<&spa::utils::dict::DictRef>) + 'static,
122 {
123 self.cbs.global = Some(Box::new(global));
124 self
125 }
126
127 #[must_use]
128 pub fn global_remove<F>(mut self, global_remove: F) -> Self
129 where
130 F: Fn(u32) + 'static,
131 {
132 self.cbs.global_remove = Some(Box::new(global_remove));
133 self
134 }
135
136 #[must_use]
137 pub fn register(self) -> Listener {
138 unsafe extern "C" fn registry_events_global(
139 data: *mut c_void,
140 id: u32,
141 permissions: u32,
142 type_: *const c_char,
143 version: u32,
144 props: *const spa_sys::spa_dict,
145 ) {
146 let type_ = CStr::from_ptr(type_).to_str().unwrap();
147 let obj = GlobalObject::new(id, permissions, type_, version, props);
148 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
149 callbacks.global.as_ref().unwrap()(&obj);
150 }
151
152 unsafe extern "C" fn registry_events_global_remove(data: *mut c_void, id: u32) {
153 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
154 callbacks.global_remove.as_ref().unwrap()(id);
155 }
156
157 let e = unsafe {
158 let mut e: Pin<Box<pw_sys::pw_registry_events>> = Box::pin(mem::zeroed());
159 e.version = pw_sys::PW_VERSION_REGISTRY_EVENTS;
160
161 if self.cbs.global.is_some() {
162 e.global = Some(registry_events_global);
163 }
164 if self.cbs.global_remove.is_some() {
165 e.global_remove = Some(registry_events_global_remove);
166 }
167
168 e
169 };
170
171 let (listener, data) = unsafe {
172 let ptr = self.registry.as_raw_ptr();
173 let data = Box::into_raw(Box::new(self.cbs));
174 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
175 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
176
177 spa::spa_interface_call_method!(
178 ptr,
179 pw_sys::pw_registry_methods,
180 add_listener,
181 listener_ptr.cast(),
182 e.as_ref().get_ref(),
183 data as *mut _
184 );
185
186 (listener, Box::from_raw(data))
187 };
188
189 Listener {
190 events: e,
191 listener,
192 data,
193 }
194 }
195}
196
197#[derive(Debug)]
198pub struct GlobalObject<P: AsRef<spa::utils::dict::DictRef>> {
199 pub id: u32,
200 pub permissions: PermissionFlags,
201 pub type_: ObjectType,
202 pub version: u32,
203 pub props: Option<P>,
204}
205
206impl GlobalObject<&spa::utils::dict::DictRef> {
207 unsafe fn new(
208 id: u32,
209 permissions: u32,
210 type_: &str,
211 version: u32,
212 props: *const spa_sys::spa_dict,
213 ) -> Self {
214 let type_ = ObjectType::from_str(type_);
215 let permissions = PermissionFlags::from_bits_retain(permissions);
216 let props = ptr::NonNull::new(props.cast_mut())
217 .map(|ptr| ptr.cast::<spa::utils::dict::DictRef>().as_ref());
218
219 Self {
220 id,
221 permissions,
222 type_,
223 version,
224 props,
225 }
226 }
227}
228
229impl<P: AsRef<spa::utils::dict::DictRef>> GlobalObject<P> {
230 pub fn to_owned(&self) -> GlobalObject<PropertiesBox> {
231 GlobalObject {
232 id: self.id,
233 permissions: self.permissions,
234 type_: self.type_.clone(),
235 version: self.version,
236 props: self
237 .props
238 .as_ref()
239 .map(|props| PropertiesBox::from_dict(props.as_ref())),
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 #[test]
248 fn set_object_type() {
249 assert_eq!(
250 ObjectType::from_str("PipeWire:Interface:Client"),
251 ObjectType::Client
252 );
253 assert_eq!(ObjectType::Client.to_str(), "PipeWire:Interface:Client");
254 assert_eq!(ObjectType::Client.client_version(), 3);
255
256 let o = ObjectType::Other("PipeWire:Interface:Badger".to_string());
257 assert_eq!(ObjectType::from_str("PipeWire:Interface:Badger"), o);
258 assert_eq!(o.to_str(), "PipeWire:Interface:Badger");
259 }
260
261 #[test]
262 #[should_panic(expected = "Invalid object type")]
263 fn client_version_panic() {
264 let o = ObjectType::Other("PipeWire:Interface:Badger".to_string());
265 assert_eq!(o.client_version(), 0);
266 }
267}