use std::os::raw::{c_char, c_void};
use std::ffi::{CStr, CString};
use std::ptr::{null, null_mut};
use std::marker::PhantomData;
use crate::error::PAErr;
pub(crate) use capi::pa_proplist as ProplistInternal;
pub use capi::pa_update_mode_t as UpdateMode;
pub mod properties {
use capi;
pub use capi::PA_PROP_MEDIA_NAME as MEDIA_NAME;
pub use capi::PA_PROP_MEDIA_TITLE as MEDIA_TITLE;
pub use capi::PA_PROP_MEDIA_ARTIST as MEDIA_ARTIST;
pub use capi::PA_PROP_MEDIA_COPYRIGHT as MEDIA_COPYRIGHT;
pub use capi::PA_PROP_MEDIA_SOFTWARE as MEDIA_SOFTWARE;
pub use capi::PA_PROP_MEDIA_LANGUAGE as MEDIA_LANGUAGE;
pub use capi::PA_PROP_MEDIA_FILENAME as MEDIA_FILENAME;
pub use capi::PA_PROP_MEDIA_ICON as MEDIA_ICON;
pub use capi::PA_PROP_MEDIA_ICON_NAME as MEDIA_ICON_NAME;
pub use capi::PA_PROP_MEDIA_ROLE as MEDIA_ROLE;
pub use capi::PA_PROP_FILTER_WANT as FILTER_WANT;
pub use capi::PA_PROP_EVENT_ID as EVENT_ID;
pub use capi::PA_PROP_EVENT_DESCRIPTION as EVENT_DESCRIPTION;
pub use capi::PA_PROP_EVENT_MOUSE_X as EVENT_MOUSE_X;
pub use capi::PA_PROP_EVENT_MOUSE_Y as EVENT_MOUSE_Y;
pub use capi::PA_PROP_EVENT_MOUSE_HPOS as EVENT_MOUSE_HPOS;
pub use capi::PA_PROP_EVENT_MOUSE_VPOS as EVENT_MOUSE_VPOS;
pub use capi::PA_PROP_EVENT_MOUSE_BUTTON as EVENT_MOUSE_BUTTON;
pub use capi::PA_PROP_WINDOW_NAME as WINDOW_NAME;
pub use capi::PA_PROP_WINDOW_ID as WINDOW_ID;
pub use capi::PA_PROP_WINDOW_ICON as WINDOW_ICON;
pub use capi::PA_PROP_WINDOW_ICON_NAME as WINDOW_ICON_NAME;
pub use capi::PA_PROP_WINDOW_X as WINDOW_X;
pub use capi::PA_PROP_WINDOW_Y as WINDOW_Y;
pub use capi::PA_PROP_WINDOW_WIDTH as WINDOW_WIDTH;
pub use capi::PA_PROP_WINDOW_HEIGHT as WINDOW_HEIGHT;
pub use capi::PA_PROP_WINDOW_HPOS as WINDOW_HPOS;
pub use capi::PA_PROP_WINDOW_VPOS as WINDOW_VPOS;
pub use capi::PA_PROP_WINDOW_DESKTOP as WINDOW_DESKTOP;
pub use capi::PA_PROP_WINDOW_X11_DISPLAY as WINDOW_X11_DISPLAY;
pub use capi::PA_PROP_WINDOW_X11_SCREEN as WINDOW_X11_SCREEN;
pub use capi::PA_PROP_WINDOW_X11_MONITOR as WINDOW_X11_MONITOR;
pub use capi::PA_PROP_WINDOW_X11_XID as WINDOW_X11_XID;
pub use capi::PA_PROP_APPLICATION_NAME as APPLICATION_NAME;
pub use capi::PA_PROP_APPLICATION_ID as APPLICATION_ID;
pub use capi::PA_PROP_APPLICATION_VERSION as APPLICATION_VERSION;
pub use capi::PA_PROP_APPLICATION_ICON as APPLICATION_ICON;
pub use capi::PA_PROP_APPLICATION_ICON_NAME as APPLICATION_ICON_NAME;
pub use capi::PA_PROP_APPLICATION_LANGUAGE as APPLICATION_LANGUAGE;
pub use capi::PA_PROP_APPLICATION_PROCESS_ID as APPLICATION_PROCESS_ID;
pub use capi::PA_PROP_APPLICATION_PROCESS_BINARY as APPLICATION_PROCESS_BINARY;
pub use capi::PA_PROP_APPLICATION_PROCESS_USER as APPLICATION_PROCESS_USER;
pub use capi::PA_PROP_APPLICATION_PROCESS_HOST as APPLICATION_PROCESS_HOST;
pub use capi::PA_PROP_APPLICATION_PROCESS_MACHINE_ID as APPLICATION_PROCESS_MACHINE_ID;
pub use capi::PA_PROP_APPLICATION_PROCESS_SESSION_ID as APPLICATION_PROCESS_SESSION_ID;
pub use capi::PA_PROP_DEVICE_STRING as DEVICE_STRING;
pub use capi::PA_PROP_DEVICE_API as DEVICE_API;
pub use capi::PA_PROP_DEVICE_DESCRIPTION as DEVICE_DESCRIPTION;
pub use capi::PA_PROP_DEVICE_BUS_PATH as DEVICE_BUS_PATH;
pub use capi::PA_PROP_DEVICE_SERIAL as DEVICE_SERIAL;
pub use capi::PA_PROP_DEVICE_VENDOR_ID as DEVICE_VENDOR_ID;
pub use capi::PA_PROP_DEVICE_VENDOR_NAME as DEVICE_VENDOR_NAME;
pub use capi::PA_PROP_DEVICE_PRODUCT_ID as DEVICE_PRODUCT_ID;
pub use capi::PA_PROP_DEVICE_PRODUCT_NAME as DEVICE_PRODUCT_NAME;
pub use capi::PA_PROP_DEVICE_CLASS as DEVICE_CLASS;
pub use capi::PA_PROP_DEVICE_FORM_FACTOR as DEVICE_FORM_FACTOR;
pub use capi::PA_PROP_DEVICE_BUS as DEVICE_BUS;
pub use capi::PA_PROP_DEVICE_ICON as DEVICE_ICON;
pub use capi::PA_PROP_DEVICE_ICON_NAME as DEVICE_ICON_NAME;
pub use capi::PA_PROP_DEVICE_ACCESS_MODE as DEVICE_ACCESS_MODE;
pub use capi::PA_PROP_DEVICE_MASTER_DEVICE as DEVICE_MASTER_DEVICE;
pub use capi::PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE as DEVICE_BUFFERING_BUFFER_SIZE;
pub use capi::PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE as DEVICE_BUFFERING_FRAGMENT_SIZE;
pub use capi::PA_PROP_DEVICE_PROFILE_NAME as DEVICE_PROFILE_NAME;
pub use capi::PA_PROP_DEVICE_PROFILE_DESCRIPTION as DEVICE_PROFILE_DESCRIPTION;
pub use capi::PA_PROP_MODULE_AUTHOR as MODULE_AUTHOR;
pub use capi::PA_PROP_MODULE_DESCRIPTION as MODULE_DESCRIPTION;
pub use capi::PA_PROP_MODULE_USAGE as MODULE_USAGE;
pub use capi::PA_PROP_MODULE_VERSION as MODULE_VERSION;
pub use capi::PA_PROP_FORMAT_RATE as FORMAT_RATE;
pub use capi::PA_PROP_FORMAT_CHANNELS as FORMAT_CHANNELS;
#[cfg(any(doc, feature = "pa_v15"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
pub use capi::PA_PROP_CONTEXT_FORCE_DISABLE_SHM as CONTEXT_FORCE_DISABLE_SHM;
#[cfg(any(doc, feature = "pa_v15"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
pub use capi::PA_PROP_BLUETOOTH_CODEC as BLUETOOTH_CODEC;
pub const FILTER_APPLY: &str = capi::PA_PROP_FILTER_APPLY;
pub const FILTER_SUPPRESS: &str = capi::PA_PROP_FILTER_SUPPRESS;
pub const DEVICE_INTENDED_ROLES: &str = capi::PA_PROP_DEVICE_INTENDED_ROLES;
pub const FORMAT_SAMPLE_FORMAT: &str = capi::PA_PROP_FORMAT_SAMPLE_FORMAT;
pub const FORMAT_CHANNEL_MAP: &str = capi::PA_PROP_FORMAT_CHANNEL_MAP;
}
pub struct Proplist(pub(crate) ProplistInner);
unsafe impl Send for Proplist {}
unsafe impl Sync for Proplist {}
pub(crate) struct ProplistInner {
pub(crate) ptr: *mut ProplistInternal,
weak: bool,
}
impl std::fmt::Debug for Proplist {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[{}]", self.to_string_sep(", ").unwrap())
}
}
pub struct Iterator<'a> {
pl_ref: ProplistInner,
state: *mut c_void,
phantom: PhantomData<&'a ProplistInner>,
}
impl Iterator<'_> {
fn new(pl: *mut ProplistInternal) -> Self {
Self {
pl_ref: ProplistInner { ptr: pl, weak: true },
state: null_mut::<c_void>(),
phantom: PhantomData,
}
}
}
impl std::iter::Iterator for Iterator<'_> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
let state_actual = &mut self.state as *mut *mut c_void;
let key_ptr = unsafe { capi::pa_proplist_iterate(self.pl_ref.ptr, state_actual) };
if key_ptr.is_null() {
return None;
}
Some(unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() })
}
}
impl IntoIterator for Proplist {
type Item = String;
type IntoIter = Iterator<'static>;
fn into_iter(mut self) -> Self::IntoIter {
assert_eq!(false, self.0.weak);
let mut iter = Iterator::new(self.0.ptr);
iter.pl_ref.weak = false;
self.0.weak = true;
iter
}
}
impl PartialEq for Proplist {
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe { capi::pa_proplist_equal(self.0.ptr, other.0.ptr) != 0 }
}
}
impl Proplist {
pub fn new() -> Option<Self> {
let ptr = unsafe { capi::pa_proplist_new() };
match ptr.is_null() {
false => Some(Self::from_raw(ptr)),
true => None,
}
}
pub fn new_from_string(s: &str) -> Option<Self> {
let c_str = CString::new(s).unwrap();
let ptr = unsafe { capi::pa_proplist_from_string(c_str.as_ptr()) };
match ptr.is_null() {
false => Some(Self::from_raw(ptr)),
true => None,
}
}
#[inline]
pub(crate) fn from_raw(ptr: *mut ProplistInternal) -> Self {
assert_eq!(false, ptr.is_null());
Proplist(ProplistInner { ptr: ptr, weak: false })
}
#[inline]
pub(crate) fn from_raw_weak(ptr: *mut ProplistInternal) -> Self {
assert_eq!(false, ptr.is_null());
Proplist(ProplistInner { ptr: ptr, weak: true })
}
#[inline]
pub(crate) fn to_owned(&self) -> Self {
Self::from_raw(unsafe { capi::pa_proplist_copy(self.0.ptr) })
}
pub fn key_is_valid(key: &str) -> bool {
let c_key = CString::new(key).unwrap();
unsafe { capi::pa_proplist_key_valid(c_key.as_ptr()) != 0 }
}
pub fn set_str(&mut self, key: &str, value: &str) -> Result<(), ()> {
let c_key = CString::new(key).unwrap();
let c_value = CString::new(value).unwrap();
match unsafe { capi::pa_proplist_sets(self.0.ptr, c_key.as_ptr(), c_value.as_ptr()) } {
0 => Ok(()),
_ => Err(()),
}
}
pub fn set_pl(&mut self, pair: &str) -> Result<(), ()> {
let c_pair = CString::new(pair).unwrap();
match unsafe { capi::pa_proplist_setp(self.0.ptr, c_pair.as_ptr()) } {
0 => Ok(()),
_ => Err(()),
}
}
pub fn set(&mut self, key: &str, data: &[u8]) -> Result<(), ()> {
let c_key = CString::new(key).unwrap();
match unsafe { capi::pa_proplist_set(self.0.ptr, c_key.as_ptr(),
data.as_ptr() as *mut c_void, data.len()) }
{
0 => Ok(()),
_ => Err(()),
}
}
pub fn get_str(&self, key: &str) -> Option<String> {
let c_key = CString::new(key).unwrap();
let ptr = unsafe { capi::pa_proplist_gets(self.0.ptr, c_key.as_ptr()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
true => None,
}
}
pub fn get(&self, key: &str) -> Option<&[u8]> {
let c_key = CString::new(key).unwrap();
let mut data_ptr = null::<c_void>();
let mut nbytes: usize = 0;
if unsafe { capi::pa_proplist_get(self.0.ptr, c_key.as_ptr(), &mut data_ptr, &mut nbytes) }
!= 0
{
return None;
}
if data_ptr.is_null() {
return None;
}
Some(unsafe { std::slice::from_raw_parts(data_ptr as *const u8, nbytes) })
}
#[inline]
pub fn merge(&mut self, other: &Self, mode: UpdateMode) {
unsafe { capi::pa_proplist_update(self.0.ptr, mode, other.0.ptr); }
}
pub fn unset(&mut self, key: &str) -> Result<(), PAErr> {
let c_key = CString::new(key).unwrap();
match unsafe { capi::pa_proplist_unset(self.0.ptr, c_key.as_ptr()) } {
0 => Ok(()),
e => Err(PAErr(e)),
}
}
pub fn unset_many(&mut self, keys: &[&str]) -> Option<u32> {
let mut c_keys: Vec<CString> = Vec::with_capacity(keys.len());
for k in keys {
c_keys.push(CString::new(*k).unwrap());
}
let mut c_keys_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1);
for k in &c_keys {
c_keys_ptrs.push(k.as_ptr());
}
c_keys_ptrs.push(null());
match unsafe { capi::pa_proplist_unset_many(self.0.ptr, c_keys_ptrs.as_ptr()) } {
r if r < 0 => None,
r => Some(r as u32),
}
}
#[inline]
pub fn iter(&self) -> Iterator<'_> {
Iterator::new(self.0.ptr)
}
pub fn to_string(&self) -> Option<String> {
let ptr = unsafe { capi::pa_proplist_to_string(self.0.ptr) };
if ptr.is_null() {
return None;
}
unsafe {
let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
capi::pa_xfree(ptr as *mut c_void);
ret
}
}
pub fn to_string_sep(&self, sep: &str) -> Option<String> {
let c_sep = CString::new(sep).unwrap();
let ptr = unsafe { capi::pa_proplist_to_string_sep(self.0.ptr, c_sep.as_ptr()) };
if ptr.is_null() {
return None;
}
unsafe {
let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
capi::pa_xfree(ptr as *mut c_void);
ret
}
}
pub fn contains(&self, key: &str) -> Option<bool> {
let c_key = CString::new(key).unwrap();
match unsafe { capi::pa_proplist_contains(self.0.ptr, c_key.as_ptr()) } {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
#[inline]
pub fn clear(&mut self) {
unsafe { capi::pa_proplist_clear(self.0.ptr); }
}
#[inline]
pub fn len(&self) -> u32 {
unsafe { capi::pa_proplist_size(self.0.ptr) }
}
#[inline]
pub fn is_empty(&self) -> bool {
unsafe { capi::pa_proplist_isempty(self.0.ptr) == 0 }
}
}
impl Drop for ProplistInner {
fn drop(&mut self) {
if !self.weak {
unsafe { capi::pa_proplist_free(self.ptr) };
}
self.ptr = null_mut::<ProplistInternal>();
}
}
impl Clone for Proplist {
#[inline]
fn clone(&self) -> Self {
self.to_owned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn proplist_iter_lifetime_conv() {
let iter = {
let my_props = Proplist::new().unwrap();
my_props.into_iter()
};
for key in iter {
println!("key: {}", key);
}
}
}