Revision control

Copy as Markdown

Other Tools

// Copyright © 2017 Mozilla Foundation
//
// This program is made available under an ISC-style license. See the
// accompanying file LICENSE for details.
use ffi;
use std::ffi::CStr;
use std::mem::{forget, MaybeUninit};
use std::os::raw::{c_int, c_void};
use std::ptr;
use util::UnwrapCStr;
use *;
// A note about `wrapped` functions
//
// C FFI demands `unsafe extern fn(*mut pa_context, ...) -> i32`, etc,
// but we want to allow such callbacks to be safe. This means no
// `unsafe` or `extern`, and callbacks should be called with a safe
// wrapper of `*mut pa_context`. Since the callback doesn't take
// ownership, this is `&Context`. `fn wrapped<T>(...)` defines a
// function that converts from our safe signature to the unsafe
// signature.
//
// Currently, we use a property of Rust, namely that each function
// gets its own unique type. These unique types can't be written
// directly, so we use generic and a type parameter, and let the Rust
// compiler fill in the name for us:
//
// fn get_sink_input_info<CB>(&self, ..., _: CB, ...) -> ...
// where CB: Fn(&Context, *const SinkInputInfo, i32, *mut c_void)
//
// Because we aren't storing or passing any state, we assert, at run-time :-(,
// that our functions are zero-sized:
//
// assert!(mem::size_of::<F>() == 0);
//
// We need to obtain a value of type F in order to call it. Since we
// can't name the function, we have to unsafely construct that value
// somehow - we do this using mem::uninitialized. Then, we call that
// function with a reference to the Context, and save the result:
//
// | generate value || call it |
// let result = ::std::mem::uninitialized::<F>()(&mut object);
//
// Lastly, since our Object is an owned type, we need to avoid
// dropping it, then return the result we just generated.
//
// mem::forget(object);
// result
// Aid in returning Operation from callbacks
macro_rules! op_or_err {
($self_:ident, $e:expr) => {{
let o = unsafe { $e };
if o.is_null() {
Err(ErrorCode::from_error_code($self_.errno()))
} else {
Ok(unsafe { operation::from_raw_ptr(o) })
}
}};
}
#[repr(C)]
#[derive(Debug)]
pub struct Context(*mut ffi::pa_context);
impl Context {
pub fn new<'a, OPT>(api: &MainloopApi, name: OPT) -> Option<Self>
where
OPT: Into<Option<&'a CStr>>,
{
let ptr = unsafe { ffi::pa_context_new(api.raw_mut(), name.unwrap_cstr()) };
if ptr.is_null() {
None
} else {
Some(Context(ptr))
}
}
#[doc(hidden)]
pub fn raw_mut(&self) -> &mut ffi::pa_context {
unsafe { &mut *self.0 }
}
pub fn unref(self) {
unsafe {
ffi::pa_context_unref(self.raw_mut());
}
}
pub fn clear_state_callback(&self) {
unsafe {
ffi::pa_context_set_state_callback(self.raw_mut(), None, ptr::null_mut());
}
}
pub fn set_state_callback<CB>(&self, _: CB, userdata: *mut c_void)
where
CB: Fn(&Context, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(c: *mut ffi::pa_context, userdata: *mut c_void)
where
F: Fn(&Context, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, userdata);
forget(ctx);
result
}
unsafe {
ffi::pa_context_set_state_callback(self.raw_mut(), Some(wrapped::<CB>), userdata);
}
}
pub fn errno(&self) -> ffi::pa_error_code_t {
unsafe { ffi::pa_context_errno(self.raw_mut()) }
}
pub fn get_state(&self) -> ContextState {
ContextState::try_from(unsafe { ffi::pa_context_get_state(self.raw_mut()) })
.expect("pa_context_get_state returned invalid ContextState")
}
pub fn connect<'a, OPT>(
&self,
server: OPT,
flags: ContextFlags,
api: *const ffi::pa_spawn_api,
) -> Result<()>
where
OPT: Into<Option<&'a CStr>>,
{
let r = unsafe {
ffi::pa_context_connect(
self.raw_mut(),
server.into().unwrap_cstr(),
flags.into(),
api,
)
};
error_result!((), r)
}
pub fn disconnect(&self) {
unsafe {
ffi::pa_context_disconnect(self.raw_mut());
}
}
pub fn drain<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
where
CB: Fn(&Context, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(c: *mut ffi::pa_context, userdata: *mut c_void)
where
F: Fn(&Context, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_drain(self.raw_mut(), Some(wrapped::<CB>), userdata)
)
}
pub fn rttime_new<CB>(
&self,
usec: USec,
_: CB,
userdata: *mut c_void,
) -> *mut ffi::pa_time_event
where
CB: Fn(&MainloopApi, *mut ffi::pa_time_event, &TimeVal, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
a: *mut ffi::pa_mainloop_api,
e: *mut ffi::pa_time_event,
tv: *const TimeVal,
userdata: *mut c_void,
) where
F: Fn(&MainloopApi, *mut ffi::pa_time_event, &TimeVal, *mut c_void),
{
let api = mainloop_api::from_raw_ptr(a);
let timeval = &*tv;
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&api, e, timeval, userdata);
forget(api);
result
}
unsafe { ffi::pa_context_rttime_new(self.raw_mut(), usec, Some(wrapped::<CB>), userdata) }
}
pub fn get_server_info<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
where
CB: Fn(&Context, Option<&ServerInfo>, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
i: *const ffi::pa_server_info,
userdata: *mut c_void,
) where
F: Fn(&Context, Option<&ServerInfo>, *mut c_void),
{
let info = if i.is_null() { None } else { Some(&*i) };
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, info, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_get_server_info(self.raw_mut(), Some(wrapped::<CB>), userdata)
)
}
pub fn get_sink_info_by_name<'str, CS, CB>(
&self,
name: CS,
_: CB,
userdata: *mut c_void,
) -> Result<Operation>
where
CB: Fn(&Context, *const SinkInfo, i32, *mut c_void),
CS: Into<Option<&'str CStr>>,
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
info: *const ffi::pa_sink_info,
eol: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, *const SinkInfo, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, info, eol, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_get_sink_info_by_name(
self.raw_mut(),
name.into().unwrap_cstr(),
Some(wrapped::<CB>),
userdata
)
)
}
pub fn get_sink_info_list<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
where
CB: Fn(&Context, *const SinkInfo, i32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
info: *const ffi::pa_sink_info,
eol: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, *const SinkInfo, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, info, eol, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_get_sink_info_list(self.raw_mut(), Some(wrapped::<CB>), userdata)
)
}
pub fn get_sink_input_info<CB>(
&self,
idx: u32,
_: CB,
userdata: *mut c_void,
) -> Result<Operation>
where
CB: Fn(&Context, *const SinkInputInfo, i32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
info: *const ffi::pa_sink_input_info,
eol: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, *const SinkInputInfo, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, info, eol, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_get_sink_input_info(self.raw_mut(), idx, Some(wrapped::<CB>), userdata)
)
}
pub fn get_source_info_list<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
where
CB: Fn(&Context, *const SourceInfo, i32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
info: *const ffi::pa_source_info,
eol: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, *const SourceInfo, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, info, eol, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_get_source_info_list(self.raw_mut(), Some(wrapped::<CB>), userdata)
)
}
pub fn set_sink_input_volume<CB>(
&self,
idx: u32,
volume: &CVolume,
_: CB,
userdata: *mut c_void,
) -> Result<Operation>
where
CB: Fn(&Context, i32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
success: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, success, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_set_sink_input_volume(
self.raw_mut(),
idx,
volume,
Some(wrapped::<CB>),
userdata
)
)
}
pub fn subscribe<CB>(
&self,
m: SubscriptionMask,
_: CB,
userdata: *mut c_void,
) -> Result<Operation>
where
CB: Fn(&Context, i32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
success: c_int,
userdata: *mut c_void,
) where
F: Fn(&Context, i32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, success, userdata);
forget(ctx);
result
}
op_or_err!(
self,
ffi::pa_context_subscribe(self.raw_mut(), m.into(), Some(wrapped::<CB>), userdata)
)
}
pub fn clear_subscribe_callback(&self) {
unsafe {
ffi::pa_context_set_subscribe_callback(self.raw_mut(), None, ptr::null_mut());
}
}
pub fn set_subscribe_callback<CB>(&self, _: CB, userdata: *mut c_void)
where
CB: Fn(&Context, SubscriptionEvent, u32, *mut c_void),
{
assert_eq!(::std::mem::size_of::<CB>(), 0);
// See: A note about `wrapped` functions
unsafe extern "C" fn wrapped<F>(
c: *mut ffi::pa_context,
t: ffi::pa_subscription_event_type_t,
idx: u32,
userdata: *mut c_void,
) where
F: Fn(&Context, SubscriptionEvent, u32, *mut c_void),
{
let ctx = context::from_raw_ptr(c);
let event = SubscriptionEvent::try_from(t)
.expect("pa_context_subscribe_cb_t passed invalid pa_subscription_event_type_t");
let cb = MaybeUninit::<F>::uninit();
let result = (*cb.as_ptr())(&ctx, event, idx, userdata);
forget(ctx);
result
}
unsafe {
ffi::pa_context_set_subscribe_callback(self.raw_mut(), Some(wrapped::<CB>), userdata);
}
}
}
#[doc(hidden)]
pub unsafe fn from_raw_ptr(ptr: *mut ffi::pa_context) -> Context {
Context(ptr)
}