libpulse_binding/
proplist.rs1use std::os::raw::{c_char, c_void};
17use std::ffi::{CStr, CString};
18use std::ptr::{null, null_mut};
19use std::marker::PhantomData;
20use crate::error::PAErr;
21
22pub(crate) use capi::pa_proplist as ProplistInternal;
23pub use capi::pa_update_mode_t as UpdateMode;
24
25pub mod properties {
27 use capi;
28
29 pub use capi::PA_PROP_MEDIA_NAME as MEDIA_NAME;
30 pub use capi::PA_PROP_MEDIA_TITLE as MEDIA_TITLE;
31 pub use capi::PA_PROP_MEDIA_ARTIST as MEDIA_ARTIST;
32 pub use capi::PA_PROP_MEDIA_COPYRIGHT as MEDIA_COPYRIGHT;
33 pub use capi::PA_PROP_MEDIA_SOFTWARE as MEDIA_SOFTWARE;
34 pub use capi::PA_PROP_MEDIA_LANGUAGE as MEDIA_LANGUAGE;
35 pub use capi::PA_PROP_MEDIA_FILENAME as MEDIA_FILENAME;
36 pub use capi::PA_PROP_MEDIA_ICON as MEDIA_ICON;
37 pub use capi::PA_PROP_MEDIA_ICON_NAME as MEDIA_ICON_NAME;
38 pub use capi::PA_PROP_MEDIA_ROLE as MEDIA_ROLE;
39 pub use capi::PA_PROP_FILTER_WANT as FILTER_WANT;
40 pub use capi::PA_PROP_EVENT_ID as EVENT_ID;
41 pub use capi::PA_PROP_EVENT_DESCRIPTION as EVENT_DESCRIPTION;
42 pub use capi::PA_PROP_EVENT_MOUSE_X as EVENT_MOUSE_X;
43 pub use capi::PA_PROP_EVENT_MOUSE_Y as EVENT_MOUSE_Y;
44 pub use capi::PA_PROP_EVENT_MOUSE_HPOS as EVENT_MOUSE_HPOS;
45 pub use capi::PA_PROP_EVENT_MOUSE_VPOS as EVENT_MOUSE_VPOS;
46 pub use capi::PA_PROP_EVENT_MOUSE_BUTTON as EVENT_MOUSE_BUTTON;
47 pub use capi::PA_PROP_WINDOW_NAME as WINDOW_NAME;
48 pub use capi::PA_PROP_WINDOW_ID as WINDOW_ID;
49 pub use capi::PA_PROP_WINDOW_ICON as WINDOW_ICON;
50 pub use capi::PA_PROP_WINDOW_ICON_NAME as WINDOW_ICON_NAME;
51 pub use capi::PA_PROP_WINDOW_X as WINDOW_X;
52 pub use capi::PA_PROP_WINDOW_Y as WINDOW_Y;
53 pub use capi::PA_PROP_WINDOW_WIDTH as WINDOW_WIDTH;
54 pub use capi::PA_PROP_WINDOW_HEIGHT as WINDOW_HEIGHT;
55 pub use capi::PA_PROP_WINDOW_HPOS as WINDOW_HPOS;
56 pub use capi::PA_PROP_WINDOW_VPOS as WINDOW_VPOS;
57 pub use capi::PA_PROP_WINDOW_DESKTOP as WINDOW_DESKTOP;
58 pub use capi::PA_PROP_WINDOW_X11_DISPLAY as WINDOW_X11_DISPLAY;
59 pub use capi::PA_PROP_WINDOW_X11_SCREEN as WINDOW_X11_SCREEN;
60 pub use capi::PA_PROP_WINDOW_X11_MONITOR as WINDOW_X11_MONITOR;
61 pub use capi::PA_PROP_WINDOW_X11_XID as WINDOW_X11_XID;
62 pub use capi::PA_PROP_APPLICATION_NAME as APPLICATION_NAME;
63 pub use capi::PA_PROP_APPLICATION_ID as APPLICATION_ID;
64 pub use capi::PA_PROP_APPLICATION_VERSION as APPLICATION_VERSION;
65 pub use capi::PA_PROP_APPLICATION_ICON as APPLICATION_ICON;
66 pub use capi::PA_PROP_APPLICATION_ICON_NAME as APPLICATION_ICON_NAME;
67 pub use capi::PA_PROP_APPLICATION_LANGUAGE as APPLICATION_LANGUAGE;
68 pub use capi::PA_PROP_APPLICATION_PROCESS_ID as APPLICATION_PROCESS_ID;
69 pub use capi::PA_PROP_APPLICATION_PROCESS_BINARY as APPLICATION_PROCESS_BINARY;
70 pub use capi::PA_PROP_APPLICATION_PROCESS_USER as APPLICATION_PROCESS_USER;
71 pub use capi::PA_PROP_APPLICATION_PROCESS_HOST as APPLICATION_PROCESS_HOST;
72 pub use capi::PA_PROP_APPLICATION_PROCESS_MACHINE_ID as APPLICATION_PROCESS_MACHINE_ID;
73 pub use capi::PA_PROP_APPLICATION_PROCESS_SESSION_ID as APPLICATION_PROCESS_SESSION_ID;
74 pub use capi::PA_PROP_DEVICE_STRING as DEVICE_STRING;
75 pub use capi::PA_PROP_DEVICE_API as DEVICE_API;
76 pub use capi::PA_PROP_DEVICE_DESCRIPTION as DEVICE_DESCRIPTION;
77 pub use capi::PA_PROP_DEVICE_BUS_PATH as DEVICE_BUS_PATH;
78 pub use capi::PA_PROP_DEVICE_SERIAL as DEVICE_SERIAL;
79 pub use capi::PA_PROP_DEVICE_VENDOR_ID as DEVICE_VENDOR_ID;
80 pub use capi::PA_PROP_DEVICE_VENDOR_NAME as DEVICE_VENDOR_NAME;
81 pub use capi::PA_PROP_DEVICE_PRODUCT_ID as DEVICE_PRODUCT_ID;
82 pub use capi::PA_PROP_DEVICE_PRODUCT_NAME as DEVICE_PRODUCT_NAME;
83 pub use capi::PA_PROP_DEVICE_CLASS as DEVICE_CLASS;
84 pub use capi::PA_PROP_DEVICE_FORM_FACTOR as DEVICE_FORM_FACTOR;
85 pub use capi::PA_PROP_DEVICE_BUS as DEVICE_BUS;
86 pub use capi::PA_PROP_DEVICE_ICON as DEVICE_ICON;
87 pub use capi::PA_PROP_DEVICE_ICON_NAME as DEVICE_ICON_NAME;
88 pub use capi::PA_PROP_DEVICE_ACCESS_MODE as DEVICE_ACCESS_MODE;
89 pub use capi::PA_PROP_DEVICE_MASTER_DEVICE as DEVICE_MASTER_DEVICE;
90 pub use capi::PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE as DEVICE_BUFFERING_BUFFER_SIZE;
91 pub use capi::PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE as DEVICE_BUFFERING_FRAGMENT_SIZE;
92 pub use capi::PA_PROP_DEVICE_PROFILE_NAME as DEVICE_PROFILE_NAME;
93 pub use capi::PA_PROP_DEVICE_PROFILE_DESCRIPTION as DEVICE_PROFILE_DESCRIPTION;
94 pub use capi::PA_PROP_MODULE_AUTHOR as MODULE_AUTHOR;
95 pub use capi::PA_PROP_MODULE_DESCRIPTION as MODULE_DESCRIPTION;
96 pub use capi::PA_PROP_MODULE_USAGE as MODULE_USAGE;
97 pub use capi::PA_PROP_MODULE_VERSION as MODULE_VERSION;
98 pub use capi::PA_PROP_FORMAT_RATE as FORMAT_RATE;
99 pub use capi::PA_PROP_FORMAT_CHANNELS as FORMAT_CHANNELS;
100 #[cfg(any(doc, feature = "pa_v15"))]
101 #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
102 pub use capi::PA_PROP_CONTEXT_FORCE_DISABLE_SHM as CONTEXT_FORCE_DISABLE_SHM;
103 #[cfg(any(doc, feature = "pa_v15"))]
104 #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
105 pub use capi::PA_PROP_BLUETOOTH_CODEC as BLUETOOTH_CODEC;
106
107 pub const FILTER_APPLY: &str = capi::PA_PROP_FILTER_APPLY;
115
116 pub const FILTER_SUPPRESS: &str = capi::PA_PROP_FILTER_SUPPRESS;
120
121 pub const DEVICE_INTENDED_ROLES: &str = capi::PA_PROP_DEVICE_INTENDED_ROLES;
124
125 pub const FORMAT_SAMPLE_FORMAT: &str = capi::PA_PROP_FORMAT_SAMPLE_FORMAT;
128
129 pub const FORMAT_CHANNEL_MAP: &str = capi::PA_PROP_FORMAT_CHANNEL_MAP;
132}
133
134pub struct Proplist(pub(crate) ProplistInner);
137
138unsafe impl Send for Proplist {}
139unsafe impl Sync for Proplist {}
140
141pub(crate) struct ProplistInner {
144 pub(crate) ptr: *mut ProplistInternal,
146 weak: bool,
148}
149
150impl std::fmt::Debug for Proplist {
151 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
152 write!(f, "[{}]", self.to_string_sep(", ").unwrap())
153 }
154}
155
156pub struct Iterator<'a> {
165 pl_ref: ProplistInner,
167 state: *mut c_void,
169 phantom: PhantomData<&'a ProplistInner>,
171}
172
173impl<'a> Iterator<'a> {
174 fn new(pl: *mut ProplistInternal) -> Self {
175 Self {
176 pl_ref: ProplistInner { ptr: pl, weak: true },
177 state: null_mut::<c_void>(),
178 phantom: PhantomData,
179 }
180 }
181}
182
183impl<'a> std::iter::Iterator for Iterator<'a> {
184 type Item = String;
185 fn next(&mut self) -> Option<Self::Item> {
186 let state_actual = &mut self.state as *mut *mut c_void;
187 let key_ptr = unsafe { capi::pa_proplist_iterate(self.pl_ref.ptr, state_actual) };
188 if key_ptr.is_null() {
189 return None;
190 }
191 Some(unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() })
193 }
194}
195
196impl IntoIterator for Proplist {
197 type Item = String;
198 type IntoIter = Iterator<'static>;
199
200 fn into_iter(mut self) -> Self::IntoIter {
201 let mut iter = Iterator::new(self.0.ptr);
202 iter.pl_ref.weak = self.0.weak;
204 self.0.weak = true;
205 iter
206 }
207}
208
209impl PartialEq for Proplist {
210 #[inline]
211 fn eq(&self, other: &Self) -> bool {
212 unsafe { capi::pa_proplist_equal(self.0.ptr, other.0.ptr) != 0 }
213 }
214}
215
216impl Proplist {
217 pub fn new() -> Option<Self> {
219 let ptr = unsafe { capi::pa_proplist_new() };
220 match ptr.is_null() {
221 false => Some(Self::from_raw(ptr)),
222 true => None,
223 }
224 }
225
226 pub fn new_from_string(s: &str) -> Option<Self> {
228 let c_str = CString::new(s).unwrap();
231 let ptr = unsafe { capi::pa_proplist_from_string(c_str.as_ptr()) };
232 match ptr.is_null() {
233 false => Some(Self::from_raw(ptr)),
234 true => None,
235 }
236 }
237
238 #[inline]
240 pub(crate) fn from_raw(ptr: *mut ProplistInternal) -> Self {
241 assert_eq!(false, ptr.is_null());
242 Proplist(ProplistInner { ptr: ptr, weak: false })
243 }
244
245 #[inline]
249 pub(crate) fn from_raw_weak(ptr: *mut ProplistInternal) -> Self {
250 assert_eq!(false, ptr.is_null());
251 Proplist(ProplistInner { ptr: ptr, weak: true })
252 }
253
254 pub fn key_is_valid(key: &str) -> bool {
256 let c_key = CString::new(key).unwrap();
259 unsafe { capi::pa_proplist_key_valid(c_key.as_ptr()) != 0 }
260 }
261
262 pub fn set_str(&mut self, key: &str, value: &str) -> Result<(), ()> {
267 let c_key = CString::new(key).unwrap();
270 let c_value = CString::new(value).unwrap();
271 match unsafe { capi::pa_proplist_sets(self.0.ptr, c_key.as_ptr(), c_value.as_ptr()) } {
272 0 => Ok(()),
273 _ => Err(()),
274 }
275 }
276
277 pub fn set_pl(&mut self, pair: &str) -> Result<(), ()> {
284 let c_pair = CString::new(pair).unwrap();
287 match unsafe { capi::pa_proplist_setp(self.0.ptr, c_pair.as_ptr()) } {
288 0 => Ok(()),
289 _ => Err(()),
290 }
291 }
292
293 pub fn set(&mut self, key: &str, data: &[u8]) -> Result<(), ()> {
298 let c_key = CString::new(key).unwrap();
301 match unsafe { capi::pa_proplist_set(self.0.ptr, c_key.as_ptr(),
302 data.as_ptr() as *mut c_void, data.len()) }
303 {
304 0 => Ok(()),
305 _ => Err(()),
306 }
307 }
308
309 pub fn get_str(&self, key: &str) -> Option<String> {
313 let c_key = CString::new(key).unwrap();
316 let ptr = unsafe { capi::pa_proplist_gets(self.0.ptr, c_key.as_ptr()) };
317 match ptr.is_null() {
318 false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
319 true => None,
320 }
321 }
322
323 pub fn get(&self, key: &str) -> Option<&[u8]> {
333 let c_key = CString::new(key).unwrap();
336 let mut data_ptr = null::<c_void>();
337 let mut nbytes: usize = 0;
338 if unsafe { capi::pa_proplist_get(self.0.ptr, c_key.as_ptr(), &mut data_ptr, &mut nbytes) }
339 != 0
340 {
341 return None;
342 }
343 if data_ptr.is_null() {
344 return None;
345 }
346 Some(unsafe { std::slice::from_raw_parts(data_ptr as *const u8, nbytes) })
347 }
348
349 #[inline]
351 pub fn merge(&mut self, other: &Self, mode: UpdateMode) {
352 unsafe { capi::pa_proplist_update(self.0.ptr, mode, other.0.ptr); }
353 }
354
355 pub fn unset(&mut self, key: &str) -> Result<(), PAErr> {
357 let c_key = CString::new(key).unwrap();
360 match unsafe { capi::pa_proplist_unset(self.0.ptr, c_key.as_ptr()) } {
361 0 => Ok(()),
362 e => Err(PAErr(e)),
363 }
364 }
365
366 pub fn unset_many(&mut self, keys: &[&str]) -> Option<u32> {
371 let mut c_keys: Vec<CString> = Vec::with_capacity(keys.len());
374 for k in keys {
375 c_keys.push(CString::new(*k).unwrap());
376 }
377
378 let mut c_keys_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1);
381 for k in &c_keys {
382 c_keys_ptrs.push(k.as_ptr());
383 }
384 c_keys_ptrs.push(null());
385
386 match unsafe { capi::pa_proplist_unset_many(self.0.ptr, c_keys_ptrs.as_ptr()) } {
387 r if r < 0 => None,
388 r => Some(r as u32),
389 }
390 }
391
392 #[inline]
413 pub fn iter(&self) -> Iterator<'_> {
414 Iterator::new(self.0.ptr)
415 }
416
417 pub fn to_string(&self) -> Option<String> {
422 let ptr = unsafe { capi::pa_proplist_to_string(self.0.ptr) };
423 if ptr.is_null() {
424 return None;
425 }
426 unsafe {
429 let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
430 capi::pa_xfree(ptr as *mut c_void);
431 ret
432 }
433 }
434
435 pub fn to_string_sep(&self, sep: &str) -> Option<String> {
437 let c_sep = CString::new(sep).unwrap();
440 let ptr = unsafe { capi::pa_proplist_to_string_sep(self.0.ptr, c_sep.as_ptr()) };
441 if ptr.is_null() {
442 return None;
443 }
444 unsafe {
447 let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
448 capi::pa_xfree(ptr as *mut c_void);
449 ret
450 }
451 }
452
453 pub fn contains(&self, key: &str) -> Option<bool> {
458 let c_key = CString::new(key).unwrap();
461 match unsafe { capi::pa_proplist_contains(self.0.ptr, c_key.as_ptr()) } {
462 0 => Some(false),
463 1 => Some(true),
464 _ => None,
465 }
466 }
467
468 #[inline]
470 pub fn clear(&mut self) {
471 unsafe { capi::pa_proplist_clear(self.0.ptr); }
472 }
473
474 #[inline]
476 pub fn len(&self) -> u32 {
477 unsafe { capi::pa_proplist_size(self.0.ptr) }
478 }
479
480 #[inline]
482 pub fn is_empty(&self) -> bool {
483 unsafe { capi::pa_proplist_isempty(self.0.ptr) == 0 }
484 }
485}
486
487impl Drop for ProplistInner {
488 fn drop(&mut self) {
489 if !self.weak {
490 unsafe { capi::pa_proplist_free(self.ptr) };
491 }
492 self.ptr = null_mut::<ProplistInternal>();
493 }
494}
495
496impl Clone for Proplist {
497 #[inline]
501 fn clone(&self) -> Self {
502 Self::from_raw(unsafe { capi::pa_proplist_copy(self.0.ptr) })
503 }
504}
505
506#[cfg(test)]
507mod tests {
508 use super::*;
509
510 #[test]
513 #[cfg(compile_fail)]
514 fn proplist_iter_lifetime() {
515 let iter = {
516 let my_props = Proplist::new().unwrap();
517 my_props.iter() };
519
520 for key in iter {
521 println!("key: {}", key);
523 }
524 }
525
526 #[test]
528 fn proplist_iter_lifetime_conv() {
529 let iter = {
530 let my_props = Proplist::new().unwrap();
531 my_props.into_iter()
532 };
533
534 for key in iter {
535 println!("key: {}", key);
537 }
538 }
539}