1use bitflags::bitflags;
5use libc::{c_char, c_void};
6use std::{
7 ffi::{CStr, CString},
8 fmt, mem,
9 pin::Pin,
10 ptr,
11};
12
13use crate::{
14 proxy::{Proxy, ProxyT},
15 registry::RegistryBox,
16 Error,
17};
18use spa::{
19 spa_interface_call_method,
20 utils::result::{AsyncSeq, SpaResult},
21};
22
23mod box_;
24pub use box_::*;
25mod rc;
26pub use rc::*;
27
28pub const PW_ID_CORE: u32 = pw_sys::PW_ID_CORE;
29
30#[repr(transparent)]
31pub struct Core(pw_sys::pw_core);
32
33impl Core {
34 pub fn as_raw(&self) -> &pw_sys::pw_core {
35 &self.0
36 }
37
38 pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_core {
39 std::ptr::addr_of!(self.0).cast_mut()
40 }
41
42 #[must_use]
44 pub fn add_listener_local(&self) -> ListenerLocalBuilder<'_> {
45 ListenerLocalBuilder {
46 core: self,
47 cbs: ListenerLocalCallbacks::default(),
48 }
49 }
50
51 pub fn get_registry(&self) -> Result<RegistryBox<'_>, Error> {
52 unsafe {
53 let registry = spa_interface_call_method!(
54 self.as_raw_ptr(),
55 pw_sys::pw_core_methods,
56 get_registry,
57 pw_sys::PW_VERSION_REGISTRY,
58 0
59 );
60 let registry = ptr::NonNull::new(registry).ok_or(Error::CreationFailed)?;
61 Ok(RegistryBox::from_raw(registry))
62 }
63 }
64
65 pub fn sync(&self, seq: i32) -> Result<AsyncSeq, Error> {
66 let res = unsafe {
67 spa_interface_call_method!(
68 self.as_raw_ptr(),
69 pw_sys::pw_core_methods,
70 sync,
71 PW_ID_CORE,
72 seq
73 )
74 };
75
76 let res = SpaResult::from_c(res).into_async_result()?;
77 Ok(res)
78 }
79
80 pub fn create_object<P: ProxyT>(
129 &self,
130 factory_name: &str,
131 properties: &impl AsRef<spa::utils::dict::DictRef>,
132 ) -> Result<P, Error> {
133 let factory_name = CString::new(factory_name).expect("Null byte in factory_name parameter");
134 let factory_name_cstr = factory_name.as_c_str();
135 self.create_object_cstr(factory_name_cstr, properties)
136 }
137
138 pub fn create_object_cstr<P: ProxyT>(
139 &self,
140 factory_name: &CStr,
141 properties: &impl AsRef<spa::utils::dict::DictRef>,
142 ) -> Result<P, Error> {
143 let type_ = P::type_();
144 let type_str = CString::new(type_.to_string())
145 .expect("Null byte in string representation of type_ parameter");
146
147 let res = unsafe {
148 spa_interface_call_method!(
149 self.as_raw_ptr(),
150 pw_sys::pw_core_methods,
151 create_object,
152 factory_name.as_ptr(),
153 type_str.as_ptr(),
154 type_.client_version(),
155 properties.as_ref().as_raw_ptr(),
156 0
157 )
158 };
159
160 let ptr = ptr::NonNull::new(res.cast()).ok_or(Error::CreationFailed)?;
161
162 Proxy::new(ptr).downcast().map_err(|(_, e)| e)
163 }
164
165 pub fn destroy_object<P: ProxyT>(&self, proxy: P) -> Result<AsyncSeq, Error> {
169 let res = unsafe {
170 spa_interface_call_method!(
171 self.as_raw_ptr(),
172 pw_sys::pw_core_methods,
173 destroy,
174 proxy.upcast_ref().as_ptr() as *mut c_void
175 )
176 };
177
178 let res = SpaResult::from_c(res).into_async_result()?;
179 Ok(res)
180 }
181}
182
183#[derive(Default)]
184struct ListenerLocalCallbacks {
185 #[allow(clippy::type_complexity)]
186 info: Option<Box<dyn Fn(&Info)>>,
187 done: Option<Box<dyn Fn(u32, AsyncSeq)>>,
188 #[allow(clippy::type_complexity)]
189 error: Option<Box<dyn Fn(u32, i32, i32, &str)>>, }
192
193pub struct ListenerLocalBuilder<'a> {
194 core: &'a Core,
195 cbs: ListenerLocalCallbacks,
196}
197
198pub struct Listener {
199 #[allow(dead_code)]
201 events: Pin<Box<pw_sys::pw_core_events>>,
202 listener: Pin<Box<spa_sys::spa_hook>>,
203 #[allow(dead_code)]
204 data: Box<ListenerLocalCallbacks>,
205}
206
207impl Listener {
208 pub fn unregister(self) {
209 }
211}
212
213impl Drop for Listener {
214 fn drop(&mut self) {
215 spa::utils::hook::remove(*self.listener);
216 }
217}
218
219impl<'a> ListenerLocalBuilder<'a> {
220 #[must_use]
221 pub fn info<F>(mut self, info: F) -> Self
222 where
223 F: Fn(&Info) + 'static,
224 {
225 self.cbs.info = Some(Box::new(info));
226 self
227 }
228
229 #[must_use]
230 pub fn done<F>(mut self, done: F) -> Self
231 where
232 F: Fn(u32, AsyncSeq) + 'static,
233 {
234 self.cbs.done = Some(Box::new(done));
235 self
236 }
237
238 #[must_use]
239 pub fn error<F>(mut self, error: F) -> Self
240 where
241 F: Fn(u32, i32, i32, &str) + 'static,
242 {
243 self.cbs.error = Some(Box::new(error));
244 self
245 }
246
247 #[must_use]
248 pub fn register(self) -> Listener {
249 unsafe extern "C" fn core_events_info(
250 data: *mut c_void,
251 info: *const pw_sys::pw_core_info,
252 ) {
253 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
254 let info = Info::new(ptr::NonNull::new(info as *mut _).expect("info is NULL"));
255 callbacks.info.as_ref().unwrap()(&info);
256 }
257
258 unsafe extern "C" fn core_events_done(data: *mut c_void, id: u32, seq: i32) {
259 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
260 callbacks.done.as_ref().unwrap()(id, AsyncSeq::from_raw(seq));
261 }
262
263 unsafe extern "C" fn core_events_error(
264 data: *mut c_void,
265 id: u32,
266 seq: i32,
267 res: i32,
268 message: *const c_char,
269 ) {
270 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
271 let message = CStr::from_ptr(message).to_str().unwrap();
272 callbacks.error.as_ref().unwrap()(id, seq, res, message);
273 }
274
275 let e = unsafe {
276 let mut e: Pin<Box<pw_sys::pw_core_events>> = Box::pin(mem::zeroed());
277 e.version = pw_sys::PW_VERSION_CORE_EVENTS;
278
279 if self.cbs.info.is_some() {
280 e.info = Some(core_events_info);
281 }
282 if self.cbs.done.is_some() {
283 e.done = Some(core_events_done);
284 }
285 if self.cbs.error.is_some() {
286 e.error = Some(core_events_error);
287 }
288
289 e
290 };
291
292 let (listener, data) = unsafe {
293 let ptr = self.core.as_raw_ptr();
294 let data = Box::into_raw(Box::new(self.cbs));
295 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
296 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
300
301 spa_interface_call_method!(
302 ptr,
303 pw_sys::pw_core_methods,
304 add_listener,
305 listener_ptr.cast(),
306 e.as_ref().get_ref(),
307 data as *mut _
308 );
309
310 (listener, Box::from_raw(data))
311 };
312
313 Listener {
314 events: e,
315 listener,
316 data,
317 }
318 }
319}
320
321pub struct Info {
322 ptr: ptr::NonNull<pw_sys::pw_core_info>,
323}
324
325impl Info {
326 fn new(info: ptr::NonNull<pw_sys::pw_core_info>) -> Self {
327 Self { ptr: info }
328 }
329
330 pub fn id(&self) -> u32 {
331 unsafe { self.ptr.as_ref().id }
332 }
333
334 pub fn cookie(&self) -> u32 {
335 unsafe { self.ptr.as_ref().cookie }
336 }
337
338 pub fn user_name(&self) -> &str {
339 unsafe {
340 CStr::from_ptr(self.ptr.as_ref().user_name)
341 .to_str()
342 .unwrap()
343 }
344 }
345
346 pub fn host_name(&self) -> &str {
347 unsafe {
348 CStr::from_ptr(self.ptr.as_ref().host_name)
349 .to_str()
350 .unwrap()
351 }
352 }
353
354 pub fn version(&self) -> &str {
355 unsafe { CStr::from_ptr(self.ptr.as_ref().version).to_str().unwrap() }
356 }
357
358 pub fn name(&self) -> &str {
359 unsafe { CStr::from_ptr(self.ptr.as_ref().name).to_str().unwrap() }
360 }
361
362 pub fn change_mask(&self) -> ChangeMask {
363 let mask = unsafe { self.ptr.as_ref().change_mask };
364 ChangeMask::from_bits_retain(mask)
365 }
366
367 pub fn props(&self) -> Option<&spa::utils::dict::DictRef> {
368 let props_ptr: *mut spa::utils::dict::DictRef = unsafe { self.ptr.as_ref().props.cast() };
369
370 ptr::NonNull::new(props_ptr).map(|ptr| unsafe { ptr.as_ref() })
371 }
372}
373
374impl fmt::Debug for Info {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 f.debug_struct("CoreInfo")
377 .field("id", &self.id())
378 .field("cookie", &self.cookie())
379 .field("user-name", &self.user_name())
380 .field("host-name", &self.host_name())
381 .field("version", &self.version())
382 .field("name", &self.name())
383 .field("change-mask", &self.change_mask())
384 .field("props", &self.props())
385 .finish()
386 }
387}
388
389bitflags! {
390 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
391 pub struct ChangeMask: u64 {
392 const PROPS = pw_sys::PW_CORE_CHANGE_MASK_PROPS as u64;
393 }
394}