1use std::ffi::CString;
5use std::os::raw::c_char;
6use std::{
7 ffi::{c_void, CStr},
8 mem,
9 pin::Pin,
10 ptr,
11};
12
13use crate::{
14 proxy::{Listener, Proxy, ProxyT},
15 types::ObjectType,
16};
17use spa::spa_interface_call_method;
18
19#[derive(Debug)]
21pub struct Metadata {
22 proxy: Proxy,
23}
24
25impl ProxyT for Metadata {
26 fn type_() -> ObjectType {
27 ObjectType::Metadata
28 }
29
30 fn upcast(self) -> Proxy {
31 self.proxy
32 }
33
34 fn upcast_ref(&self) -> &Proxy {
35 &self.proxy
36 }
37
38 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
39 where
40 Self: Sized,
41 {
42 Self { proxy }
43 }
44}
45
46impl Metadata {
47 #[must_use = "Use the builder to register event callbacks"]
48 pub fn add_listener_local(&self) -> MetadataListenerLocalBuilder<'_> {
49 MetadataListenerLocalBuilder {
50 metadata: self,
51 cbs: ListenerLocalCallbacks::default(),
52 }
53 }
54
55 pub fn set_property(&self, subject: u32, key: &str, type_: Option<&str>, value: Option<&str>) {
62 let key = CString::new(key).expect("Invalid byte in metadata key");
64 let type_ = type_.map(|t| CString::new(t).expect("Invalid byte in metadata type"));
65 let value = value.map(|v| CString::new(v).expect("Invalid byte in metadata value"));
66 let key_cstr = key.as_c_str();
67
68 Metadata::set_property_cstr(self, subject, key_cstr, type_.as_deref(), value.as_deref())
69 }
70
71 pub fn set_property_cstr(
78 &self,
79 subject: u32,
80 key: &CStr,
81 type_: Option<&CStr>,
82 value: Option<&CStr>,
83 ) {
84 unsafe {
85 spa::spa_interface_call_method!(
86 self.proxy.as_ptr(),
87 pw_sys::pw_metadata_methods,
88 set_property,
89 subject,
90 key.as_ptr() as *const _,
91 type_.map_or_else(ptr::null, CStr::as_ptr) as *const _,
92 value.map_or_else(ptr::null, CStr::as_ptr) as *const _
93 );
94 }
95 }
96
97 pub fn clear(&self) {
103 unsafe {
104 spa::spa_interface_call_method!(
105 self.proxy.as_ptr(),
106 pw_sys::pw_metadata_methods,
107 clear,
108 );
109 }
110 }
111}
112
113#[must_use = "Listeners unregister themselves when dropped. Keep the listener alive in order to receive events."]
131pub struct MetadataListener {
132 #[allow(dead_code)]
134 events: Pin<Box<pw_sys::pw_metadata_events>>,
135 listener: Pin<Box<spa_sys::spa_hook>>,
136 #[allow(dead_code)]
137 data: Box<ListenerLocalCallbacks>,
138}
139
140impl Listener for MetadataListener {}
141
142impl Drop for MetadataListener {
143 fn drop(&mut self) {
144 spa::utils::hook::remove(*self.listener);
145 }
146}
147
148#[derive(Default)]
149struct ListenerLocalCallbacks {
150 #[allow(clippy::type_complexity)]
151 property: Option<Box<dyn Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32>>,
152}
153
154pub struct MetadataListenerLocalBuilder<'meta> {
171 metadata: &'meta Metadata,
172 cbs: ListenerLocalCallbacks,
173}
174
175impl<'meta> MetadataListenerLocalBuilder<'meta> {
176 #[must_use = "Call `.register()` to start receiving events"]
197 pub fn property<F>(mut self, property: F) -> Self
198 where
199 F: Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32 + 'static,
200 {
201 self.cbs.property = Some(Box::new(property));
202 self
203 }
204
205 pub fn register(self) -> MetadataListener {
207 unsafe extern "C" fn metadata_events_property(
208 data: *mut c_void,
209 subject: u32,
210 key: *const c_char,
211 type_: *const c_char,
212 value: *const c_char,
213 ) -> i32 {
214 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
215 let key = if !key.is_null() {
216 Some(CStr::from_ptr(key).to_string_lossy())
217 } else {
218 None
219 };
220 let type_ = if !type_.is_null() {
221 Some(CStr::from_ptr(type_).to_string_lossy())
222 } else {
223 None
224 };
225 let value = if !value.is_null() {
226 Some(CStr::from_ptr(value).to_string_lossy())
227 } else {
228 None
229 };
230 callbacks.property.as_ref().unwrap()(
231 subject,
232 key.as_deref(),
233 type_.as_deref(),
234 value.as_deref(),
235 )
236 }
237
238 let e = unsafe {
239 let mut e: Pin<Box<pw_sys::pw_metadata_events>> = Box::pin(mem::zeroed());
240 e.version = pw_sys::PW_VERSION_METADATA_EVENTS;
241
242 if self.cbs.property.is_some() {
243 e.property = Some(metadata_events_property);
244 }
245
246 e
247 };
248
249 let (listener, data) = unsafe {
250 let metadata = &self.metadata.proxy.as_ptr();
251
252 let data = Box::into_raw(Box::new(self.cbs));
253 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
254 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
255
256 spa_interface_call_method!(
257 metadata,
258 pw_sys::pw_metadata_methods,
259 add_listener,
260 listener_ptr.cast(),
261 e.as_ref().get_ref(),
262 data as *mut _
263 );
264
265 (listener, Box::from_raw(data))
266 };
267
268 MetadataListener {
269 events: e,
270 listener,
271 data,
272 }
273 }
274}