pub mod ext_device_manager;
pub mod ext_device_restore;
pub mod ext_stream_restore;
pub mod introspect;
pub mod scache;
pub mod subscribe;
use std;
use capi;
use std::os::raw::{c_char, c_void};
use std::ffi::{CStr, CString};
use std::ptr::{null, null_mut};
use std::rc::Rc;
use mainloop::api::MainloopInnerType;
use mainloop::events::timer::{TimeEvent, TimeEventRef};
use operation::Operation;
use error::PAErr;
use time::MonotonicTs;
use proplist::Proplist;
use callbacks::box_closure_get_capi_ptr;
use capi::pa_context as ContextInternal;
pub struct Context {
pub(crate) ptr: *mut ContextInternal,
weak: bool,
cb_ptrs: CallbackPointers,
}
unsafe impl Send for Context {}
unsafe impl Sync for Context {}
#[derive(Default)]
struct CallbackPointers {
set_state: NotifyCb,
subscribe: self::subscribe::Callback,
event: EventCb,
}
type NotifyCb = ::callbacks::MultiUseCallback<dyn FnMut(),
extern "C" fn(*mut ContextInternal, *mut c_void)>;
type EventCb = ::callbacks::MultiUseCallback<dyn FnMut(String, Proplist),
extern "C" fn(*mut ContextInternal, name: *const c_char, pl: *mut ::proplist::ProplistInternal,
*mut c_void)>;
type ExtSubscribeCb = ::callbacks::MultiUseCallback<dyn FnMut(),
extern "C" fn(*mut ContextInternal, *mut c_void)>;
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum State {
Unconnected,
Connecting,
Authorizing,
SettingName,
Ready,
Failed,
Terminated,
}
#[test]
fn state_compare_capi(){
assert_eq!(std::mem::size_of::<State>(), std::mem::size_of::<capi::pa_context_state_t>());
assert_eq!(std::mem::align_of::<State>(), std::mem::align_of::<capi::pa_context_state_t>());
}
impl From<State> for capi::pa_context_state_t {
#[inline]
fn from(s: State) -> Self {
unsafe { std::mem::transmute(s) }
}
}
impl From<capi::pa_context_state_t> for State {
#[inline]
fn from(s: capi::pa_context_state_t) -> Self {
unsafe { std::mem::transmute(s) }
}
}
impl State {
pub fn is_good(self) -> bool {
self == State::Connecting ||
self == State::Authorizing ||
self == State::SettingName ||
self == State::Ready
}
}
pub type FlagSet = capi::pa_context_flags_t;
pub mod flags {
use capi;
use super::FlagSet;
pub const NOFLAGS: FlagSet = capi::PA_CONTEXT_NOFLAGS;
pub const NOAUTOSPAWN: FlagSet = capi::PA_CONTEXT_NOAUTOSPAWN;
pub const NOFAIL: FlagSet = capi::PA_CONTEXT_NOFAIL;
}
impl Context {
pub fn new(mainloop: &impl ::mainloop::api::Mainloop, name: &str) -> Option<Self> {
let c_name = CString::new(name.clone()).unwrap();
let ptr = unsafe { capi::pa_context_new(mainloop.inner().get_api().as_ref(),
c_name.as_ptr()) };
match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None }
}
pub fn new_with_proplist(mainloop: &impl ::mainloop::api::Mainloop, name: &str,
proplist: &Proplist) -> Option<Self>
{
let c_name = CString::new(name.clone()).unwrap();
let ptr = unsafe { capi::pa_context_new_with_proplist(mainloop.inner().get_api().as_ref(),
c_name.as_ptr(), proplist.0.ptr) };
match ptr.is_null() { false => Some(Self::from_raw(ptr)), true => None }
}
#[inline]
pub(crate) fn from_raw(ptr: *mut ContextInternal) -> Self {
assert_eq!(false, ptr.is_null());
Self { ptr: ptr, weak: false, cb_ptrs: Default::default() }
}
pub fn set_state_callback(&mut self, callback: Option<Box<dyn FnMut() + 'static>>) {
let saved = &mut self.cb_ptrs.set_state;
*saved = NotifyCb::new(callback);
let (cb_fn, cb_data) = saved.get_capi_params(notify_cb_proxy_multi);
unsafe { capi::pa_context_set_state_callback(self.ptr, cb_fn, cb_data); }
}
pub fn set_event_callback(&mut self,
callback: Option<Box<dyn FnMut(String, Proplist) + 'static>>)
{
let saved = &mut self.cb_ptrs.event;
*saved = EventCb::new(callback);
let (cb_fn, cb_data) = saved.get_capi_params(event_cb_proxy);
unsafe { capi::pa_context_set_event_callback(self.ptr, cb_fn, cb_data); }
}
#[inline]
pub fn errno(&self) -> PAErr {
PAErr(unsafe { capi::pa_context_errno(self.ptr) })
}
#[inline]
pub fn is_pending(&self) -> bool {
unsafe { capi::pa_context_is_pending(self.ptr) != 0 }
}
#[inline]
pub fn get_state(&self) -> State {
unsafe { capi::pa_context_get_state(self.ptr).into() }
}
pub fn connect(&mut self, server: Option<&str>, flags: FlagSet, api: Option<&::def::SpawnApi>)
-> Result<(), PAErr>
{
let c_server = match server {
Some(server) => CString::new(server.clone()).unwrap(),
None => CString::new("").unwrap(),
};
let p_api = api.map_or(null::<capi::pa_spawn_api>(), |a| a.as_ref());
let p_server = server.map_or(null::<c_char>(), |_| c_server.as_ptr() as *const c_char);
match unsafe { capi::pa_context_connect(self.ptr, p_server, flags, p_api) } {
0 => Ok(()),
e => Err(PAErr(e)),
}
}
#[inline]
pub fn disconnect(&mut self) {
unsafe { capi::pa_context_disconnect(self.ptr); }
}
pub fn drain<F>(&mut self, callback: F) -> Option<Operation<dyn FnMut()>>
where F: FnMut() + 'static
{
let cb_data = box_closure_get_capi_ptr::<dyn FnMut()>(Box::new(callback));
let ptr = unsafe { capi::pa_context_drain(self.ptr, Some(notify_cb_proxy_single), cb_data) };
match ptr.is_null() {
false => Some(Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut()>)),
true => None,
}
}
pub fn exit_daemon<F>(&mut self, callback: F) -> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_exit_daemon(self.ptr, Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn set_default_sink<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let c_name = CString::new(name.clone()).unwrap();
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_set_default_sink(self.ptr, c_name.as_ptr(),
Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn set_default_source<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let c_name = CString::new(name.clone()).unwrap();
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_set_default_source(self.ptr, c_name.as_ptr(),
Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn is_local(&self) -> Option<bool> {
match unsafe { capi::pa_context_is_local(self.ptr) } {
1 => Some(true),
0 => Some(false),
_ => None,
}
}
pub fn set_name<F>(&mut self, name: &str, callback: F) -> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let c_name = CString::new(name.clone()).unwrap();
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_set_name(self.ptr, c_name.as_ptr(),
Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn get_server(&self) -> Option<String> {
let ptr = unsafe { capi::pa_context_get_server(self.ptr) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
true => None,
}
}
#[inline]
pub fn get_protocol_version(&self) -> u32 {
unsafe { capi::pa_context_get_protocol_version(self.ptr) }
}
pub fn get_server_protocol_version(&self) -> Option<u32> {
match unsafe { capi::pa_context_get_server_protocol_version(self.ptr) } {
::def::INVALID_INDEX => None,
r => Some(r),
}
}
pub fn proplist_update<F>(&mut self, mode: ::proplist::UpdateMode, pl: &Proplist, callback: F)
-> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_proplist_update(self.ptr, mode, pl.0.ptr,
Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn proplist_remove<F>(&mut self, keys: &[&str], callback: F) -> Operation<dyn FnMut(bool)>
where F: FnMut(bool) + 'static
{
let mut c_keys: Vec<CString> = Vec::with_capacity(keys.len());
for key in keys {
c_keys.push(CString::new(key.clone()).unwrap());
}
let mut c_key_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1);
for c_key in c_keys {
c_key_ptrs.push(c_key.as_ptr());
}
c_key_ptrs.push(null());
let cb_data = box_closure_get_capi_ptr::<dyn FnMut(bool)>(Box::new(callback));
let ptr = unsafe { capi::pa_context_proplist_remove(self.ptr, c_key_ptrs.as_ptr(),
Some(success_cb_proxy), cb_data) };
assert!(!ptr.is_null());
Operation::from_raw(ptr, cb_data as *mut Box<dyn FnMut(bool)>)
}
pub fn get_index(&self) -> Option<u32> {
match unsafe { capi::pa_context_get_index(self.ptr) } {
::def::INVALID_INDEX => None,
r => Some(r),
}
}
pub fn rttime_new<T, F>(&self, mainloop: &impl (::mainloop::api::Mainloop<MI=T::MI>),
time: MonotonicTs, mut callback: F) -> Option<TimeEvent<T::MI>>
where T: ::mainloop::api::Mainloop + 'static,
F: FnMut(TimeEventRef<T::MI>) + 'static
{
let inner_for_wrapper = mainloop.inner();
let wrapper_cb = Box::new(move |ptr| {
let ref_obj = TimeEventRef::<T::MI>::from_raw(ptr, Rc::clone(&inner_for_wrapper));
callback(ref_obj);
});
let to_save = ::mainloop::events::timer::EventCb::new(Some(wrapper_cb));
let (cb_fn, cb_data) = to_save.get_capi_params(::mainloop::events::timer::event_cb_proxy);
let ptr = unsafe { capi::pa_context_rttime_new(self.ptr, (time.0).0,
std::mem::transmute(cb_fn), cb_data) };
match ptr.is_null() {
false => Some(TimeEvent::<T::MI>::from_raw(ptr, mainloop.inner(), to_save)),
true => None,
}
}
pub fn get_tile_size(&self, ss: &::sample::Spec) -> Option<usize> {
match unsafe { capi::pa_context_get_tile_size(self.ptr, ss.as_ref()) } {
std::usize::MAX => None,
r => Some(r),
}
}
pub fn load_cookie_from_file(&mut self, cookie_file_path: &str) -> Result<(), PAErr> {
let c_path = CString::new(cookie_file_path.clone()).unwrap();
match unsafe { capi::pa_context_load_cookie_from_file(self.ptr, c_path.as_ptr()) } {
0 => Ok(()),
e => Err(PAErr(e)),
}
}
}
impl Drop for Context {
fn drop(&mut self) {
if !self.weak {
unsafe { capi::pa_context_unref(self.ptr) };
}
self.ptr = null_mut::<ContextInternal>();
}
}
extern "C"
fn success_cb_proxy(_: *mut ContextInternal, success: i32, userdata: *mut c_void) {
let success_actual = match success { 0 => false, _ => true };
let _ = std::panic::catch_unwind(|| {
assert!(!userdata.is_null());
let mut callback = unsafe { Box::from_raw(userdata as *mut Box<dyn FnMut(bool)>) };
(callback)(success_actual);
});
}
extern "C"
fn notify_cb_proxy_single(_: *mut ContextInternal, userdata: *mut c_void) {
let _ = std::panic::catch_unwind(|| {
assert!(!userdata.is_null());
let mut callback = unsafe { Box::from_raw(userdata as *mut Box<dyn FnMut()>) };
(callback)();
});
}
extern "C"
fn notify_cb_proxy_multi(_: *mut ContextInternal, userdata: *mut c_void) {
let _ = std::panic::catch_unwind(|| {
let callback = NotifyCb::get_callback(userdata);
(callback)();
});
}
extern "C"
fn event_cb_proxy(_: *mut ContextInternal, name: *const c_char,
proplist: *mut ::proplist::ProplistInternal, userdata: *mut c_void)
{
let _ = std::panic::catch_unwind(|| {
assert!(!name.is_null());
let n = {
let tmp = unsafe { CStr::from_ptr(name) };
tmp.to_string_lossy().into_owned()
};
let pl = Proplist::from_raw_weak(proplist);
let callback = EventCb::get_callback(userdata);
(callback)(n, pl);
});
}
extern "C"
fn ext_test_cb_proxy(_: *mut ContextInternal, version: u32, userdata: *mut c_void) {
let _ = std::panic::catch_unwind(|| {
let mut callback = ::callbacks::get_su_callback::<dyn FnMut(u32)>(userdata);
(callback)(version);
});
}
extern "C"
fn ext_subscribe_cb_proxy(_: *mut ContextInternal, userdata: *mut c_void) {
let _ = std::panic::catch_unwind(|| {
let callback = ExtSubscribeCb::get_callback(userdata);
(callback)();
});
}