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)]
20pub struct Metadata {
21 proxy: Proxy,
22}
23
24impl ProxyT for Metadata {
25 fn type_() -> ObjectType {
26 ObjectType::Metadata
27 }
28
29 fn upcast(self) -> Proxy {
30 self.proxy
31 }
32
33 fn upcast_ref(&self) -> &Proxy {
34 &self.proxy
35 }
36
37 unsafe fn from_proxy_unchecked(proxy: Proxy) -> Self
38 where
39 Self: Sized,
40 {
41 Self { proxy }
42 }
43}
44
45impl Metadata {
46 pub fn add_listener_local(&self) -> MetadataListenerLocalBuilder<'_> {
47 MetadataListenerLocalBuilder {
48 metadata: self,
49 cbs: ListenerLocalCallbacks::default(),
50 }
51 }
52
53 pub fn set_property(&self, subject: u32, key: &str, type_: Option<&str>, value: Option<&str>) {
54 let key = CString::new(key).expect("Invalid byte in metadata key");
56 let type_ = type_.map(|t| CString::new(t).expect("Invalid byte in metadata type"));
57 let value = value.map(|v| CString::new(v).expect("Invalid byte in metadata value"));
58 let key_cstr = key.as_c_str();
59
60 Metadata::set_property_cstr(self, subject, key_cstr, type_.as_deref(), value.as_deref())
61 }
62
63 pub fn set_property_cstr(
64 &self,
65 subject: u32,
66 key: &CStr,
67 type_: Option<&CStr>,
68 value: Option<&CStr>,
69 ) {
70 unsafe {
71 spa::spa_interface_call_method!(
72 self.proxy.as_ptr(),
73 pw_sys::pw_metadata_methods,
74 set_property,
75 subject,
76 key.as_ptr() as *const _,
77 type_.map_or_else(ptr::null, CStr::as_ptr) as *const _,
78 value.map_or_else(ptr::null, CStr::as_ptr) as *const _
79 );
80 }
81 }
82
83 pub fn clear(&self) {
84 unsafe {
85 spa::spa_interface_call_method!(
86 self.proxy.as_ptr(),
87 pw_sys::pw_metadata_methods,
88 clear,
89 );
90 }
91 }
92}
93
94pub struct MetadataListener {
95 #[allow(dead_code)]
97 events: Pin<Box<pw_sys::pw_metadata_events>>,
98 listener: Pin<Box<spa_sys::spa_hook>>,
99 #[allow(dead_code)]
100 data: Box<ListenerLocalCallbacks>,
101}
102
103impl Listener for MetadataListener {}
104
105impl Drop for MetadataListener {
106 fn drop(&mut self) {
107 spa::utils::hook::remove(*self.listener);
108 }
109}
110
111#[derive(Default)]
112struct ListenerLocalCallbacks {
113 #[allow(clippy::type_complexity)]
114 property: Option<Box<dyn Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32>>,
115}
116
117#[must_use]
118pub struct MetadataListenerLocalBuilder<'meta> {
119 metadata: &'meta Metadata,
120 cbs: ListenerLocalCallbacks,
121}
122
123impl<'meta> MetadataListenerLocalBuilder<'meta> {
124 pub fn property<F>(mut self, property: F) -> Self
131 where
132 F: Fn(u32, Option<&str>, Option<&str>, Option<&str>) -> i32 + 'static,
133 {
134 self.cbs.property = Some(Box::new(property));
135 self
136 }
137
138 #[must_use]
139 pub fn register(self) -> MetadataListener {
140 unsafe extern "C" fn metadata_events_property(
141 data: *mut c_void,
142 subject: u32,
143 key: *const c_char,
144 type_: *const c_char,
145 value: *const c_char,
146 ) -> i32 {
147 let callbacks = (data as *mut ListenerLocalCallbacks).as_ref().unwrap();
148 let key = if !key.is_null() {
149 Some(CStr::from_ptr(key).to_string_lossy())
150 } else {
151 None
152 };
153 let type_ = if !type_.is_null() {
154 Some(CStr::from_ptr(type_).to_string_lossy())
155 } else {
156 None
157 };
158 let value = if !value.is_null() {
159 Some(CStr::from_ptr(value).to_string_lossy())
160 } else {
161 None
162 };
163 callbacks.property.as_ref().unwrap()(
164 subject,
165 key.as_deref(),
166 type_.as_deref(),
167 value.as_deref(),
168 )
169 }
170
171 let e = unsafe {
172 let mut e: Pin<Box<pw_sys::pw_metadata_events>> = Box::pin(mem::zeroed());
173 e.version = pw_sys::PW_VERSION_METADATA_EVENTS;
174
175 if self.cbs.property.is_some() {
176 e.property = Some(metadata_events_property);
177 }
178
179 e
180 };
181
182 let (listener, data) = unsafe {
183 let metadata = &self.metadata.proxy.as_ptr();
184
185 let data = Box::into_raw(Box::new(self.cbs));
186 let mut listener: Pin<Box<spa_sys::spa_hook>> = Box::pin(mem::zeroed());
187 let listener_ptr: *mut spa_sys::spa_hook = listener.as_mut().get_unchecked_mut();
188
189 spa_interface_call_method!(
190 metadata,
191 pw_sys::pw_metadata_methods,
192 add_listener,
193 listener_ptr.cast(),
194 e.as_ref().get_ref(),
195 data as *mut _
196 );
197
198 (listener, Box::from_raw(data))
199 };
200
201 MetadataListener {
202 events: e,
203 listener,
204 data,
205 }
206 }
207}