diff options
Diffstat (limited to 'third_party/rust/pulse/src')
-rw-r--r-- | third_party/rust/pulse/src/context.rs | 487 | ||||
-rw-r--r-- | third_party/rust/pulse/src/error.rs | 54 | ||||
-rw-r--r-- | third_party/rust/pulse/src/lib.rs | 692 | ||||
-rw-r--r-- | third_party/rust/pulse/src/mainloop_api.rs | 60 | ||||
-rw-r--r-- | third_party/rust/pulse/src/operation.rs | 43 | ||||
-rw-r--r-- | third_party/rust/pulse/src/proplist.rs | 32 | ||||
-rw-r--r-- | third_party/rust/pulse/src/stream.rs | 456 | ||||
-rw-r--r-- | third_party/rust/pulse/src/threaded_mainloop.rs | 92 | ||||
-rw-r--r-- | third_party/rust/pulse/src/util.rs | 42 |
9 files changed, 1958 insertions, 0 deletions
diff --git a/third_party/rust/pulse/src/context.rs b/third_party/rust/pulse/src/context.rs new file mode 100644 index 0000000000..6c435c9c65 --- /dev/null +++ b/third_party/rust/pulse/src/context.rs @@ -0,0 +1,487 @@ +// 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) +} diff --git a/third_party/rust/pulse/src/error.rs b/third_party/rust/pulse/src/error.rs new file mode 100644 index 0000000000..2e6f7074f2 --- /dev/null +++ b/third_party/rust/pulse/src/error.rs @@ -0,0 +1,54 @@ +// 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; + +#[macro_export] +macro_rules! error_result { + ($t:expr, $err:expr) => { + if $err >= 0 { + Ok($t) + } else { + Err(ErrorCode::from_error_result($err)) + } + }; +} + +#[derive(Debug, PartialEq)] +pub struct ErrorCode { + err: ffi::pa_error_code_t, +} + +impl ErrorCode { + pub fn from_error_result(err: i32) -> Self { + debug_assert!(err < 0); + ErrorCode { + err: (-err) as ffi::pa_error_code_t, + } + } + + pub fn from_error_code(err: ffi::pa_error_code_t) -> Self { + debug_assert!(err > 0); + ErrorCode { err: err } + } + + fn desc(&self) -> &'static str { + let cstr = unsafe { CStr::from_ptr(ffi::pa_strerror(self.err)) }; + cstr.to_str().unwrap() + } +} + +impl ::std::error::Error for ErrorCode { + fn description(&self) -> &str { + self.desc() + } +} + +impl ::std::fmt::Display for ErrorCode { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{:?}: {}", self, self.desc()) + } +} diff --git a/third_party/rust/pulse/src/lib.rs b/third_party/rust/pulse/src/lib.rs new file mode 100644 index 0000000000..58f57923a4 --- /dev/null +++ b/third_party/rust/pulse/src/lib.rs @@ -0,0 +1,692 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +#[macro_use] +extern crate bitflags; +extern crate pulse_ffi as ffi; + +#[macro_use] +mod error; +mod context; +mod mainloop_api; +mod operation; +mod proplist; +mod stream; +mod threaded_mainloop; +mod util; + +pub use context::Context; +pub use error::ErrorCode; +pub use ffi::pa_buffer_attr as BufferAttr; +pub use ffi::pa_channel_map as ChannelMap; +pub use ffi::pa_cvolume as CVolume; +pub use ffi::pa_sample_spec as SampleSpec; +pub use ffi::pa_server_info as ServerInfo; +pub use ffi::pa_sink_info as SinkInfo; +pub use ffi::pa_sink_input_info as SinkInputInfo; +pub use ffi::pa_source_info as SourceInfo; +pub use ffi::pa_usec_t as USec; +pub use ffi::pa_volume_t as Volume; +pub use ffi::timeval as TimeVal; +pub use mainloop_api::MainloopApi; +pub use operation::Operation; +pub use proplist::Proplist; +use std::os::raw::{c_char, c_uint}; +pub use stream::Stream; +pub use threaded_mainloop::ThreadedMainloop; + +#[allow(non_camel_case_types)] +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SampleFormat { + Invalid = ffi::PA_SAMPLE_INVALID, + U8 = ffi::PA_SAMPLE_U8, + Alaw = ffi::PA_SAMPLE_ALAW, + Ulaw = ffi::PA_SAMPLE_ULAW, + Signed16LE = ffi::PA_SAMPLE_S16LE, + Signed16BE = ffi::PA_SAMPLE_S16BE, + Float32LE = ffi::PA_SAMPLE_FLOAT32LE, + Float32BE = ffi::PA_SAMPLE_FLOAT32BE, + Signed32LE = ffi::PA_SAMPLE_S32LE, + Signed32BE = ffi::PA_SAMPLE_S32BE, + Signed24LE = ffi::PA_SAMPLE_S24LE, + Signed24BE = ffi::PA_SAMPLE_S24BE, + Signed24_32LE = ffi::PA_SAMPLE_S24_32LE, + Signed23_32BE = ffi::PA_SAMPLE_S24_32BE, +} + +impl Default for SampleFormat { + fn default() -> Self { + SampleFormat::Invalid + } +} + +impl Into<ffi::pa_sample_format_t> for SampleFormat { + fn into(self) -> ffi::pa_sample_format_t { + self as ffi::pa_sample_format_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ContextState { + Unconnected = ffi::PA_CONTEXT_UNCONNECTED, + Connecting = ffi::PA_CONTEXT_CONNECTING, + Authorizing = ffi::PA_CONTEXT_AUTHORIZING, + SettingName = ffi::PA_CONTEXT_SETTING_NAME, + Ready = ffi::PA_CONTEXT_READY, + Failed = ffi::PA_CONTEXT_FAILED, + Terminated = ffi::PA_CONTEXT_TERMINATED, +} + +impl ContextState { + // This function implements the PA_CONTENT_IS_GOOD macro from pulse/def.h + // It must match the version from PA headers. + pub fn is_good(self) -> bool { + match self { + ContextState::Connecting + | ContextState::Authorizing + | ContextState::SettingName + | ContextState::Ready => true, + _ => false, + } + } + + pub fn try_from(x: ffi::pa_context_state_t) -> Option<Self> { + if x >= ffi::PA_CONTEXT_UNCONNECTED && x <= ffi::PA_CONTEXT_TERMINATED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for ContextState { + fn default() -> Self { + ContextState::Unconnected + } +} + +impl Into<ffi::pa_context_state_t> for ContextState { + fn into(self) -> ffi::pa_context_state_t { + self as ffi::pa_context_state_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StreamState { + Unconnected = ffi::PA_STREAM_UNCONNECTED, + Creating = ffi::PA_STREAM_CREATING, + Ready = ffi::PA_STREAM_READY, + Failed = ffi::PA_STREAM_FAILED, + Terminated = ffi::PA_STREAM_TERMINATED, +} + +impl StreamState { + // This function implements the PA_STREAM_IS_GOOD macro from pulse/def.h + // It must match the version from PA headers. + pub fn is_good(self) -> bool { + match self { + StreamState::Creating | StreamState::Ready => true, + _ => false, + } + } + + pub fn try_from(x: ffi::pa_stream_state_t) -> Option<Self> { + if x >= ffi::PA_STREAM_UNCONNECTED && x <= ffi::PA_STREAM_TERMINATED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for StreamState { + fn default() -> Self { + StreamState::Unconnected + } +} + +impl Into<ffi::pa_stream_state_t> for StreamState { + fn into(self) -> ffi::pa_stream_state_t { + self as ffi::pa_stream_state_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OperationState { + Running = ffi::PA_OPERATION_RUNNING, + Done = ffi::PA_OPERATION_DONE, + Cancelled = ffi::PA_OPERATION_CANCELLED, +} + +impl OperationState { + pub fn try_from(x: ffi::pa_operation_state_t) -> Option<Self> { + if x >= ffi::PA_OPERATION_RUNNING && x <= ffi::PA_OPERATION_CANCELLED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_operation_state_t> for OperationState { + fn into(self) -> ffi::pa_operation_state_t { + self as ffi::pa_operation_state_t + } +} + +bitflags! { + pub struct ContextFlags: u32 { + const NOAUTOSPAWN = ffi::PA_CONTEXT_NOAUTOSPAWN; + const NOFAIL = ffi::PA_CONTEXT_NOFAIL; + } +} + +impl Into<ffi::pa_context_flags_t> for ContextFlags { + fn into(self) -> ffi::pa_context_flags_t { + self.bits() as ffi::pa_context_flags_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DeviceType { + Sink = ffi::PA_DEVICE_TYPE_SINK, + Source = ffi::PA_DEVICE_TYPE_SOURCE, +} + +impl DeviceType { + pub fn try_from(x: ffi::pa_device_type_t) -> Option<Self> { + if x >= ffi::PA_DEVICE_TYPE_SINK && x <= ffi::PA_DEVICE_TYPE_SOURCE { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_device_type_t> for DeviceType { + fn into(self) -> ffi::pa_device_type_t { + self as ffi::pa_device_type_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StreamDirection { + NoDirection = ffi::PA_STREAM_NODIRECTION, + Playback = ffi::PA_STREAM_PLAYBACK, + Record = ffi::PA_STREAM_RECORD, + StreamUpload = ffi::PA_STREAM_UPLOAD, +} + +impl StreamDirection { + pub fn try_from(x: ffi::pa_stream_direction_t) -> Option<Self> { + if x >= ffi::PA_STREAM_NODIRECTION && x <= ffi::PA_STREAM_UPLOAD { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_stream_direction_t> for StreamDirection { + fn into(self) -> ffi::pa_stream_direction_t { + self as ffi::pa_stream_direction_t + } +} + +bitflags! { + pub struct StreamFlags : u32 { + const START_CORKED = ffi::PA_STREAM_START_CORKED; + const INTERPOLATE_TIMING = ffi::PA_STREAM_INTERPOLATE_TIMING; + const NOT_MONOTONIC = ffi::PA_STREAM_NOT_MONOTONIC; + const AUTO_TIMING_UPDATE = ffi::PA_STREAM_AUTO_TIMING_UPDATE; + const NO_REMAP_CHANNELS = ffi::PA_STREAM_NO_REMAP_CHANNELS; + const NO_REMIX_CHANNELS = ffi::PA_STREAM_NO_REMIX_CHANNELS; + const FIX_FORMAT = ffi::PA_STREAM_FIX_FORMAT; + const FIX_RATE = ffi::PA_STREAM_FIX_RATE; + const FIX_CHANNELS = ffi::PA_STREAM_FIX_CHANNELS; + const DONT_MOVE = ffi::PA_STREAM_DONT_MOVE; + const VARIABLE_RATE = ffi::PA_STREAM_VARIABLE_RATE; + const PEAK_DETECT = ffi::PA_STREAM_PEAK_DETECT; + const START_MUTED = ffi::PA_STREAM_START_MUTED; + const ADJUST_LATENCY = ffi::PA_STREAM_ADJUST_LATENCY; + const EARLY_REQUESTS = ffi::PA_STREAM_EARLY_REQUESTS; + const DONT_INHIBIT_AUTO_SUSPEND = ffi::PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND; + const START_UNMUTED = ffi::PA_STREAM_START_UNMUTED; + const FAIL_ON_SUSPEND = ffi::PA_STREAM_FAIL_ON_SUSPEND; + const RELATIVE_VOLUME = ffi::PA_STREAM_RELATIVE_VOLUME; + const PASSTHROUGH = ffi::PA_STREAM_PASSTHROUGH; + } +} + +impl StreamFlags { + pub fn try_from(x: ffi::pa_stream_flags_t) -> Option<Self> { + if (x & !(ffi::PA_STREAM_NOFLAGS + | ffi::PA_STREAM_START_CORKED + | ffi::PA_STREAM_INTERPOLATE_TIMING + | ffi::PA_STREAM_NOT_MONOTONIC + | ffi::PA_STREAM_AUTO_TIMING_UPDATE + | ffi::PA_STREAM_NO_REMAP_CHANNELS + | ffi::PA_STREAM_NO_REMIX_CHANNELS + | ffi::PA_STREAM_FIX_FORMAT + | ffi::PA_STREAM_FIX_RATE + | ffi::PA_STREAM_FIX_CHANNELS + | ffi::PA_STREAM_DONT_MOVE + | ffi::PA_STREAM_VARIABLE_RATE + | ffi::PA_STREAM_PEAK_DETECT + | ffi::PA_STREAM_START_MUTED + | ffi::PA_STREAM_ADJUST_LATENCY + | ffi::PA_STREAM_EARLY_REQUESTS + | ffi::PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND + | ffi::PA_STREAM_START_UNMUTED + | ffi::PA_STREAM_FAIL_ON_SUSPEND + | ffi::PA_STREAM_RELATIVE_VOLUME + | ffi::PA_STREAM_PASSTHROUGH)) + == 0 + { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_stream_flags_t> for StreamFlags { + fn into(self) -> ffi::pa_stream_flags_t { + self.bits() as ffi::pa_stream_flags_t + } +} + +pub enum StreamLatency { + Positive(u64), + Negative(u64), +} + +bitflags! { + pub struct SubscriptionMask : u32 { + const SINK = ffi::PA_SUBSCRIPTION_MASK_SINK; + const SOURCE = ffi::PA_SUBSCRIPTION_MASK_SOURCE; + const SINK_INPUT = ffi::PA_SUBSCRIPTION_MASK_SINK_INPUT; + const SOURCE_OUTPUT = ffi::PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT; + const MODULE = ffi::PA_SUBSCRIPTION_MASK_MODULE; + const CLIENT = ffi::PA_SUBSCRIPTION_MASK_CLIENT; + const SAMPLE_CACHE = ffi::PA_SUBSCRIPTION_MASK_SAMPLE_CACHE; + const SERVER = ffi::PA_SUBSCRIPTION_MASK_SERVER; + const AUTOLOAD = ffi::PA_SUBSCRIPTION_MASK_AUTOLOAD; + const CARD = ffi::PA_SUBSCRIPTION_MASK_CARD; + } +} + +impl SubscriptionMask { + pub fn try_from(x: ffi::pa_subscription_mask_t) -> Option<Self> { + if (x & !ffi::PA_SUBSCRIPTION_MASK_ALL) == 0 { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_subscription_mask_t> for SubscriptionMask { + fn into(self) -> ffi::pa_subscription_mask_t { + self.bits() as ffi::pa_subscription_mask_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SubscriptionEventFacility { + Sink = ffi::PA_SUBSCRIPTION_EVENT_SINK, + Source = ffi::PA_SUBSCRIPTION_EVENT_SOURCE, + SinkInput = ffi::PA_SUBSCRIPTION_EVENT_SINK_INPUT, + SourceOutput = ffi::PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT, + Module = ffi::PA_SUBSCRIPTION_EVENT_MODULE, + Client = ffi::PA_SUBSCRIPTION_EVENT_CLIENT, + SampleCache = ffi::PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE, + Server = ffi::PA_SUBSCRIPTION_EVENT_SERVER, + Autoload = ffi::PA_SUBSCRIPTION_EVENT_AUTOLOAD, + Card = ffi::PA_SUBSCRIPTION_EVENT_CARD, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SubscriptionEventType { + New, + Change, + Remove, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SubscriptionEvent(ffi::pa_subscription_event_type_t); +impl SubscriptionEvent { + pub fn try_from(x: ffi::pa_subscription_event_type_t) -> Option<Self> { + if (x & !(ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK | ffi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK)) + == 0 + { + Some(SubscriptionEvent(x)) + } else { + None + } + } + + pub fn event_facility(self) -> SubscriptionEventFacility { + unsafe { ::std::mem::transmute(self.0 & ffi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK) } + } + + pub fn event_type(self) -> SubscriptionEventType { + unsafe { ::std::mem::transmute((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4) } + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SeekMode { + Relative = ffi::PA_SEEK_RELATIVE, + Absolute = ffi::PA_SEEK_ABSOLUTE, + RelativeOnRead = ffi::PA_SEEK_RELATIVE_ON_READ, + RelativeEnd = ffi::PA_SEEK_RELATIVE_END, +} + +impl SeekMode { + pub fn try_from(x: ffi::pa_seek_mode_t) -> Option<Self> { + if x >= ffi::PA_SEEK_RELATIVE && x <= ffi::PA_SEEK_RELATIVE_END { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_seek_mode_t> for SeekMode { + fn into(self) -> ffi::pa_seek_mode_t { + self as ffi::pa_seek_mode_t + } +} + +bitflags! { + pub struct SinkFlags: u32 { + const HW_VOLUME_CTRL = ffi::PA_SINK_HW_VOLUME_CTRL; + const LATENCY = ffi::PA_SINK_LATENCY; + const HARDWARE = ffi::PA_SINK_HARDWARE; + const NETWORK = ffi::PA_SINK_NETWORK; + const HW_MUTE_CTRL = ffi::PA_SINK_HW_MUTE_CTRL; + const DECIBEL_VOLUME = ffi::PA_SINK_DECIBEL_VOLUME; + const FLAT_VOLUME = ffi::PA_SINK_FLAT_VOLUME; + const DYNAMIC_LATENCY = ffi::PA_SINK_DYNAMIC_LATENCY; + const SET_FORMATS = ffi::PA_SINK_SET_FORMATS; + } +} + +impl SinkFlags { + pub fn try_from(x: ffi::pa_sink_flags_t) -> Option<SinkFlags> { + if (x & !(ffi::PA_SINK_NOFLAGS + | ffi::PA_SINK_HW_VOLUME_CTRL + | ffi::PA_SINK_LATENCY + | ffi::PA_SINK_HARDWARE + | ffi::PA_SINK_NETWORK + | ffi::PA_SINK_HW_MUTE_CTRL + | ffi::PA_SINK_DECIBEL_VOLUME + | ffi::PA_SINK_DYNAMIC_LATENCY + | ffi::PA_SINK_FLAT_VOLUME + | ffi::PA_SINK_SET_FORMATS)) + == 0 + { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SinkState { + InvalidState = ffi::PA_SINK_INVALID_STATE, + Running = ffi::PA_SINK_RUNNING, + Idle = ffi::PA_SINK_IDLE, + Suspended = ffi::PA_SINK_SUSPENDED, + Init = ffi::PA_SINK_INIT, + Unlinked = ffi::PA_SINK_UNLINKED, +} + +bitflags! { + pub struct SourceFlags: u32 { + const HW_VOLUME_CTRL = ffi::PA_SOURCE_HW_VOLUME_CTRL; + const LATENCY = ffi::PA_SOURCE_LATENCY; + const HARDWARE = ffi::PA_SOURCE_HARDWARE; + const NETWORK = ffi::PA_SOURCE_NETWORK; + const HW_MUTE_CTRL = ffi::PA_SOURCE_HW_MUTE_CTRL; + const DECIBEL_VOLUME = ffi::PA_SOURCE_DECIBEL_VOLUME; + const DYNAMIC_LATENCY = ffi::PA_SOURCE_DYNAMIC_LATENCY; + const FLAT_VOLUME = ffi::PA_SOURCE_FLAT_VOLUME; + } +} + +impl Into<ffi::pa_source_flags_t> for SourceFlags { + fn into(self) -> ffi::pa_source_flags_t { + self.bits() as ffi::pa_source_flags_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SourceState { + InvalidState = ffi::PA_SOURCE_INVALID_STATE, + Running = ffi::PA_SOURCE_RUNNING, + Idle = ffi::PA_SOURCE_IDLE, + Suspended = ffi::PA_SOURCE_SUSPENDED, + Init = ffi::PA_SOURCE_INIT, + Unlinked = ffi::PA_SOURCE_UNLINKED, +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PortAvailable { + Unknown = ffi::PA_PORT_AVAILABLE_UNKNOWN, + No = ffi::PA_PORT_AVAILABLE_NO, + Yes = ffi::PA_PORT_AVAILABLE_YES, +} + +impl PortAvailable { + pub fn try_from(x: ffi::pa_port_available_t) -> Option<Self> { + if x >= ffi::PA_PORT_AVAILABLE_UNKNOWN && x <= ffi::PA_PORT_AVAILABLE_YES { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into<ffi::pa_port_available_t> for PortAvailable { + fn into(self) -> ffi::pa_port_available_t { + self as ffi::pa_port_available_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ChannelPosition { + Invalid = ffi::PA_CHANNEL_POSITION_INVALID, + Mono = ffi::PA_CHANNEL_POSITION_MONO, + FrontLeft = ffi::PA_CHANNEL_POSITION_FRONT_LEFT, + FrontRight = ffi::PA_CHANNEL_POSITION_FRONT_RIGHT, + FrontCenter = ffi::PA_CHANNEL_POSITION_FRONT_CENTER, + RearCenter = ffi::PA_CHANNEL_POSITION_REAR_CENTER, + RearLeft = ffi::PA_CHANNEL_POSITION_REAR_LEFT, + RearRight = ffi::PA_CHANNEL_POSITION_REAR_RIGHT, + LowFreqEffects = ffi::PA_CHANNEL_POSITION_LFE, + FrontLeftOfCenter = ffi::PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + FrontRightOfCenter = ffi::PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + SideLeft = ffi::PA_CHANNEL_POSITION_SIDE_LEFT, + SideRight = ffi::PA_CHANNEL_POSITION_SIDE_RIGHT, + Aux0 = ffi::PA_CHANNEL_POSITION_AUX0, + Aux1 = ffi::PA_CHANNEL_POSITION_AUX1, + Aux2 = ffi::PA_CHANNEL_POSITION_AUX2, + Aux3 = ffi::PA_CHANNEL_POSITION_AUX3, + Aux4 = ffi::PA_CHANNEL_POSITION_AUX4, + Aux5 = ffi::PA_CHANNEL_POSITION_AUX5, + Aux6 = ffi::PA_CHANNEL_POSITION_AUX6, + Aux7 = ffi::PA_CHANNEL_POSITION_AUX7, + Aux8 = ffi::PA_CHANNEL_POSITION_AUX8, + Aux9 = ffi::PA_CHANNEL_POSITION_AUX9, + Aux10 = ffi::PA_CHANNEL_POSITION_AUX10, + Aux11 = ffi::PA_CHANNEL_POSITION_AUX11, + Aux12 = ffi::PA_CHANNEL_POSITION_AUX12, + Aux13 = ffi::PA_CHANNEL_POSITION_AUX13, + Aux14 = ffi::PA_CHANNEL_POSITION_AUX14, + Aux15 = ffi::PA_CHANNEL_POSITION_AUX15, + Aux16 = ffi::PA_CHANNEL_POSITION_AUX16, + Aux17 = ffi::PA_CHANNEL_POSITION_AUX17, + Aux18 = ffi::PA_CHANNEL_POSITION_AUX18, + Aux19 = ffi::PA_CHANNEL_POSITION_AUX19, + Aux20 = ffi::PA_CHANNEL_POSITION_AUX20, + Aux21 = ffi::PA_CHANNEL_POSITION_AUX21, + Aux22 = ffi::PA_CHANNEL_POSITION_AUX22, + Aux23 = ffi::PA_CHANNEL_POSITION_AUX23, + Aux24 = ffi::PA_CHANNEL_POSITION_AUX24, + Aux25 = ffi::PA_CHANNEL_POSITION_AUX25, + Aux26 = ffi::PA_CHANNEL_POSITION_AUX26, + Aux27 = ffi::PA_CHANNEL_POSITION_AUX27, + Aux28 = ffi::PA_CHANNEL_POSITION_AUX28, + Aux29 = ffi::PA_CHANNEL_POSITION_AUX29, + Aux30 = ffi::PA_CHANNEL_POSITION_AUX30, + Aux31 = ffi::PA_CHANNEL_POSITION_AUX31, + TopCenter = ffi::PA_CHANNEL_POSITION_TOP_CENTER, + TopFrontLeft = ffi::PA_CHANNEL_POSITION_TOP_FRONT_LEFT, + TopFrontRight = ffi::PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, + TopFrontCenter = ffi::PA_CHANNEL_POSITION_TOP_FRONT_CENTER, + TopRearLeft = ffi::PA_CHANNEL_POSITION_TOP_REAR_LEFT, + TopRearRight = ffi::PA_CHANNEL_POSITION_TOP_REAR_RIGHT, + TopRearCenter = ffi::PA_CHANNEL_POSITION_TOP_REAR_CENTER, +} + +impl ChannelPosition { + pub fn try_from(x: ffi::pa_channel_position_t) -> Option<Self> { + if x >= ffi::PA_CHANNEL_POSITION_INVALID && x < ffi::PA_CHANNEL_POSITION_MAX { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for ChannelPosition { + fn default() -> Self { + ChannelPosition::Invalid + } +} + +impl Into<ffi::pa_channel_position_t> for ChannelPosition { + fn into(self) -> ffi::pa_channel_position_t { + self as ffi::pa_channel_position_t + } +} +pub type Result<T> = ::std::result::Result<T, error::ErrorCode>; + +pub trait CVolumeExt { + fn set(&mut self, channels: c_uint, v: Volume); + fn set_balance(&mut self, map: &ChannelMap, new_balance: f32); +} + +impl CVolumeExt for CVolume { + fn set(&mut self, channels: c_uint, v: Volume) { + unsafe { + ffi::pa_cvolume_set(self, channels, v); + } + } + + fn set_balance(&mut self, map: &ChannelMap, new_balance: f32) { + unsafe { + ffi::pa_cvolume_set_balance(self, map, new_balance); + } + } +} + +pub trait ChannelMapExt { + fn init() -> ChannelMap; + fn init_auto(ch: u32, def: ffi::pa_channel_map_def_t) -> Option<ChannelMap>; + fn can_balance(&self) -> bool; +} + +impl ChannelMapExt for ChannelMap { + fn init() -> ChannelMap { + let mut cm = ChannelMap::default(); + unsafe { + ffi::pa_channel_map_init(&mut cm); + } + cm + } + fn init_auto(ch: u32, def: ffi::pa_channel_map_def_t) -> Option<ChannelMap> { + let mut cm = ChannelMap::default(); + let r: *mut ffi::pa_channel_map = + unsafe { ffi::pa_channel_map_init_auto(&mut cm, ch, def) }; + if r.is_null() { + None + } else { + Some(cm) + } + } + fn can_balance(&self) -> bool { + unsafe { ffi::pa_channel_map_can_balance(self) > 0 } + } +} + +pub trait ProplistExt { + fn proplist(&self) -> Proplist; +} + +impl ProplistExt for SinkInfo { + fn proplist(&self) -> Proplist { + unsafe { proplist::from_raw_ptr(self.proplist) } + } +} + +impl ProplistExt for SourceInfo { + fn proplist(&self) -> Proplist { + unsafe { proplist::from_raw_ptr(self.proplist) } + } +} + +pub trait SampleSpecExt { + fn frame_size(&self) -> usize; + fn sample_size(&self) -> usize; +} + +impl SampleSpecExt for SampleSpec { + fn frame_size(&self) -> usize { + unsafe { ffi::pa_frame_size(self) } + } + fn sample_size(&self) -> usize { + unsafe { ffi::pa_sample_size(self) } + } +} + +pub trait USecExt { + fn to_bytes(self, spec: &SampleSpec) -> usize; +} + +impl USecExt for USec { + fn to_bytes(self, spec: &SampleSpec) -> usize { + unsafe { ffi::pa_usec_to_bytes(self, spec) } + } +} + +pub fn library_version() -> *const c_char { + unsafe { ffi::pa_get_library_version() } +} + +pub fn sw_volume_from_linear(vol: f64) -> Volume { + unsafe { ffi::pa_sw_volume_from_linear(vol) } +} + +pub fn rtclock_now() -> USec { + unsafe { ffi::pa_rtclock_now() } +} diff --git a/third_party/rust/pulse/src/mainloop_api.rs b/third_party/rust/pulse/src/mainloop_api.rs new file mode 100644 index 0000000000..4b2ad426c3 --- /dev/null +++ b/third_party/rust/pulse/src/mainloop_api.rs @@ -0,0 +1,60 @@ +// 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::mem; +use std::os::raw::c_void; + +#[allow(non_camel_case_types)] +type pa_once_cb_t = + Option<unsafe extern "C" fn(m: *mut ffi::pa_mainloop_api, userdata: *mut c_void)>; +fn wrap_once_cb<F>(_: F) -> pa_once_cb_t +where + F: Fn(&MainloopApi, *mut c_void), +{ + assert!(mem::size_of::<F>() == 0); + + unsafe extern "C" fn wrapped<F>(m: *mut ffi::pa_mainloop_api, userdata: *mut c_void) + where + F: Fn(&MainloopApi, *mut c_void), + { + let api = from_raw_ptr(m); + let result = mem::transmute::<_, &F>(&())(&api, userdata); + mem::forget(api); + result + } + + Some(wrapped::<F>) +} + +pub struct MainloopApi(*mut ffi::pa_mainloop_api); + +impl MainloopApi { + pub fn raw_mut(&self) -> &mut ffi::pa_mainloop_api { + unsafe { &mut *self.0 } + } + + pub fn once<CB>(&self, cb: CB, userdata: *mut c_void) + where + CB: Fn(&MainloopApi, *mut c_void), + { + let wrapped = wrap_once_cb(cb); + unsafe { + ffi::pa_mainloop_api_once(self.raw_mut(), wrapped, userdata); + } + } + + pub fn time_free(&self, e: *mut ffi::pa_time_event) { + unsafe { + if let Some(f) = self.raw_mut().time_free { + f(e); + } + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_mainloop_api) -> MainloopApi { + MainloopApi(raw) +} diff --git a/third_party/rust/pulse/src/operation.rs b/third_party/rust/pulse/src/operation.rs new file mode 100644 index 0000000000..0b6823b2cc --- /dev/null +++ b/third_party/rust/pulse/src/operation.rs @@ -0,0 +1,43 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; + +#[derive(Debug)] +pub struct Operation(*mut ffi::pa_operation); + +impl Operation { + pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_operation) -> Operation { + Operation(raw) + } + + pub fn cancel(&mut self) { + unsafe { + ffi::pa_operation_cancel(self.0); + } + } + + pub fn get_state(&self) -> ffi::pa_operation_state_t { + unsafe { ffi::pa_operation_get_state(self.0) } + } +} + +impl Clone for Operation { + fn clone(&self) -> Self { + Operation(unsafe { ffi::pa_operation_ref(self.0) }) + } +} + +impl Drop for Operation { + fn drop(&mut self) { + unsafe { + ffi::pa_operation_unref(self.0); + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_operation) -> Operation { + Operation::from_raw_ptr(raw) +} diff --git a/third_party/rust/pulse/src/proplist.rs b/third_party/rust/pulse/src/proplist.rs new file mode 100644 index 0000000000..460473f467 --- /dev/null +++ b/third_party/rust/pulse/src/proplist.rs @@ -0,0 +1,32 @@ +// 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, CString}; + +#[derive(Debug)] +pub struct Proplist(*mut ffi::pa_proplist); + +impl Proplist { + pub fn gets<T>(&self, key: T) -> Option<&CStr> + where + T: Into<Vec<u8>>, + { + let key = match CString::new(key) { + Ok(k) => k, + _ => return None, + }; + let r = unsafe { ffi::pa_proplist_gets(self.0, key.as_ptr()) }; + if r.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(r) }) + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_proplist) -> Proplist { + return Proplist(raw); +} diff --git a/third_party/rust/pulse/src/stream.rs b/third_party/rust/pulse/src/stream.rs new file mode 100644 index 0000000000..4548fb533a --- /dev/null +++ b/third_party/rust/pulse/src/stream.rs @@ -0,0 +1,456 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use context; +use ffi; +use operation; +use std::ffi::CStr; +use std::mem::{self, forget, MaybeUninit}; +use std::os::raw::{c_int, c_void}; +use std::ptr; +use util::*; +use *; + +#[derive(Debug)] +pub struct Stream(*mut ffi::pa_stream); + +impl Stream { + pub fn new<'a, CM>( + c: &Context, + name: &::std::ffi::CStr, + ss: &SampleSpec, + map: CM, + ) -> Option<Self> + where + CM: Into<Option<&'a ChannelMap>>, + { + let ptr = unsafe { + ffi::pa_stream_new( + c.raw_mut(), + name.as_ptr(), + ss as *const _, + to_ptr(map.into()), + ) + }; + if ptr.is_null() { + None + } else { + Some(Stream(ptr)) + } + } + + #[doc(hidden)] + pub fn raw_mut(&self) -> &mut ffi::pa_stream { + unsafe { &mut *self.0 } + } + + pub fn unref(self) { + unsafe { + ffi::pa_stream_unref(self.raw_mut()); + } + } + + pub fn get_state(&self) -> StreamState { + StreamState::try_from(unsafe { ffi::pa_stream_get_state(self.raw_mut()) }) + .expect("pa_stream_get_state returned invalid StreamState") + } + + pub fn get_context(&self) -> Option<Context> { + let ptr = unsafe { ffi::pa_stream_get_context(self.raw_mut()) }; + if ptr.is_null() { + return None; + } + + let ctx = unsafe { context::from_raw_ptr(ptr) }; + Some(ctx) + } + + pub fn get_index(&self) -> u32 { + unsafe { ffi::pa_stream_get_index(self.raw_mut()) } + } + + pub fn get_device_name<'a>(&'a self) -> Result<&'a CStr> { + let r = unsafe { ffi::pa_stream_get_device_name(self.raw_mut()) }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { CStr::from_ptr(r) }) + } + + pub fn is_suspended(&self) -> Result<bool> { + let r = unsafe { ffi::pa_stream_is_suspended(self.raw_mut()) }; + error_result!(r != 0, r) + } + + pub fn is_corked(&self) -> Result<bool> { + let r = unsafe { ffi::pa_stream_is_corked(self.raw_mut()) }; + error_result!(r != 0, r) + } + + pub fn connect_playback<'a, D, A, V, S>( + &self, + dev: D, + attr: A, + flags: StreamFlags, + volume: V, + sync_stream: S, + ) -> Result<()> + where + D: Into<Option<&'a CStr>>, + A: Into<Option<&'a BufferAttr>>, + V: Into<Option<&'a CVolume>>, + S: Into<Option<&'a mut Stream>>, + { + let r = unsafe { + ffi::pa_stream_connect_playback( + self.raw_mut(), + str_to_ptr(dev.into()), + to_ptr(attr.into()), + flags.into(), + to_ptr(volume.into()), + map_to_mut_ptr(sync_stream.into(), |p| p.0), + ) + }; + error_result!((), r) + } + + pub fn connect_record<'a, D, A>(&self, dev: D, attr: A, flags: StreamFlags) -> Result<()> + where + D: Into<Option<&'a CStr>>, + A: Into<Option<&'a BufferAttr>>, + { + let r = unsafe { + ffi::pa_stream_connect_record( + self.raw_mut(), + str_to_ptr(dev.into()), + to_ptr(attr.into()), + flags.into(), + ) + }; + error_result!((), r) + } + + pub fn disconnect(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_disconnect(self.raw_mut()) }; + error_result!((), r) + } + + pub fn begin_write(&self, req_bytes: usize) -> Result<(*mut c_void, usize)> { + let mut data: *mut c_void = ptr::null_mut(); + let mut nbytes = req_bytes; + let r = unsafe { ffi::pa_stream_begin_write(self.raw_mut(), &mut data, &mut nbytes) }; + error_result!((data, nbytes), r) + } + + pub fn cancel_write(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_cancel_write(self.raw_mut()) }; + error_result!((), r) + } + + pub fn write( + &self, + data: *const c_void, + nbytes: usize, + offset: i64, + seek: SeekMode, + ) -> Result<()> { + let r = unsafe { + ffi::pa_stream_write(self.raw_mut(), data, nbytes, None, offset, seek.into()) + }; + error_result!((), r) + } + + pub unsafe fn peek(&self, data: *mut *const c_void, length: *mut usize) -> Result<()> { + let r = ffi::pa_stream_peek(self.raw_mut(), data, length); + error_result!((), r) + } + + pub fn drop(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_drop(self.raw_mut()) }; + error_result!((), r) + } + + pub fn writable_size(&self) -> Result<usize> { + let r = unsafe { ffi::pa_stream_writable_size(self.raw_mut()) }; + if r == ::std::usize::MAX { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(r) + } + + pub fn readable_size(&self) -> Result<usize> { + let r = unsafe { ffi::pa_stream_readable_size(self.raw_mut()) }; + if r == ::std::usize::MAX { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(r) + } + + pub fn update_timing_info<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation> + where + CB: Fn(&Stream, i32, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>( + s: *mut ffi::pa_stream, + success: c_int, + userdata: *mut c_void, + ) where + F: Fn(&Stream, i32, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, success, userdata); + forget(stm); + + result + } + + let r = unsafe { + ffi::pa_stream_update_timing_info(self.raw_mut(), Some(wrapped::<CB>), userdata) + }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { operation::from_raw_ptr(r) }) + } + + pub fn clear_state_callback(&self) { + unsafe { + ffi::pa_stream_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(&Stream, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>(s: *mut ffi::pa_stream, userdata: *mut c_void) + where + F: Fn(&Stream, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_state_callback(self.raw_mut(), Some(wrapped::<CB>), userdata); + } + } + + pub fn clear_write_callback(&self) { + unsafe { + ffi::pa_stream_set_write_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_write_callback<CB>(&self, _: CB, userdata: *mut c_void) + where + CB: Fn(&Stream, usize, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>( + s: *mut ffi::pa_stream, + nbytes: usize, + userdata: *mut c_void, + ) where + F: Fn(&Stream, usize, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, nbytes, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_write_callback(self.raw_mut(), Some(wrapped::<CB>), userdata); + } + } + + pub fn clear_read_callback(&self) { + unsafe { + ffi::pa_stream_set_read_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_read_callback<CB>(&self, _: CB, userdata: *mut c_void) + where + CB: Fn(&Stream, usize, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>( + s: *mut ffi::pa_stream, + nbytes: usize, + userdata: *mut c_void, + ) where + F: Fn(&Stream, usize, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, nbytes, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_read_callback(self.raw_mut(), Some(wrapped::<CB>), userdata); + } + } + + pub fn cork<CB>(&self, b: i32, _: CB, userdata: *mut c_void) -> Result<Operation> + where + CB: Fn(&Stream, i32, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>( + s: *mut ffi::pa_stream, + success: c_int, + userdata: *mut c_void, + ) where + F: Fn(&Stream, i32, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, success, userdata); + forget(stm); + + result + } + + let r = unsafe { ffi::pa_stream_cork(self.raw_mut(), b, Some(wrapped::<CB>), userdata) }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { operation::from_raw_ptr(r) }) + } + + pub fn get_time(&self) -> Result<USec> { + let mut usec: USec = 0; + let r = unsafe { ffi::pa_stream_get_time(self.raw_mut(), &mut usec) }; + error_result!(usec, r) + } + + pub fn get_latency(&self) -> Result<StreamLatency> { + let mut usec: u64 = 0; + let mut negative: i32 = 0; + let r = unsafe { ffi::pa_stream_get_latency(self.raw_mut(), &mut usec, &mut negative) }; + error_result!( + if negative == 0 { + StreamLatency::Positive(usec) + } else { + StreamLatency::Negative(usec) + }, + r + ) + } + + pub fn get_sample_spec(&self) -> &SampleSpec { + unsafe { + let ptr = ffi::pa_stream_get_sample_spec(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } + + pub fn get_channel_map(&self) -> &ChannelMap { + unsafe { + let ptr = ffi::pa_stream_get_channel_map(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } + + pub fn get_buffer_attr(&self) -> &BufferAttr { + unsafe { + let ptr = ffi::pa_stream_get_buffer_attr(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } + + pub fn set_name<CB>(&self, name: &CStr, _: CB, userdata: *mut c_void) -> Result<Operation> + where + CB: Fn(&Stream, i32, *mut c_void), + { + assert_eq!(mem::size_of::<CB>(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped<F>( + s: *mut ffi::pa_stream, + success: c_int, + userdata: *mut c_void, + ) where + F: Fn(&Stream, i32, *mut c_void), + { + let mut stm = stream::from_raw_ptr(s); + let cb = MaybeUninit::<F>::uninit(); + let result = (*cb.as_ptr())(&mut stm, success, userdata); + forget(stm); + + result + } + + let r = unsafe { + ffi::pa_stream_set_name(self.raw_mut(), name.as_ptr(), Some(wrapped::<CB>), userdata) + }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { operation::from_raw_ptr(r) }) + } +} + +#[doc(hidden)] +pub unsafe fn from_raw_ptr(ptr: *mut ffi::pa_stream) -> Stream { + Stream(ptr) +} diff --git a/third_party/rust/pulse/src/threaded_mainloop.rs b/third_party/rust/pulse/src/threaded_mainloop.rs new file mode 100644 index 0000000000..74d2410d63 --- /dev/null +++ b/third_party/rust/pulse/src/threaded_mainloop.rs @@ -0,0 +1,92 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; +use mainloop_api; +use mainloop_api::MainloopApi; +use ErrorCode; +use Result; + +#[derive(Debug)] +pub struct ThreadedMainloop(*mut ffi::pa_threaded_mainloop); + +impl ThreadedMainloop { + pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_threaded_mainloop) -> Self { + ThreadedMainloop(raw) + } + + pub fn new() -> Self { + unsafe { ThreadedMainloop::from_raw_ptr(ffi::pa_threaded_mainloop_new()) } + } + + pub fn raw_mut(&self) -> &mut ffi::pa_threaded_mainloop { + unsafe { &mut *self.0 } + } + + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + pub fn start(&self) -> Result<()> { + match unsafe { ffi::pa_threaded_mainloop_start(self.raw_mut()) } { + 0 => Ok(()), + _ => Err(ErrorCode::from_error_code(ffi::PA_ERR_UNKNOWN)), + } + } + + pub fn stop(&self) { + unsafe { + ffi::pa_threaded_mainloop_stop(self.raw_mut()); + } + } + + pub fn lock(&self) { + unsafe { + ffi::pa_threaded_mainloop_lock(self.raw_mut()); + } + } + + pub fn unlock(&self) { + unsafe { + ffi::pa_threaded_mainloop_unlock(self.raw_mut()); + } + } + + pub fn wait(&self) { + unsafe { + ffi::pa_threaded_mainloop_wait(self.raw_mut()); + } + } + + pub fn signal(&self) { + unsafe { + ffi::pa_threaded_mainloop_signal(self.raw_mut(), 0); + } + } + + pub fn get_api(&self) -> MainloopApi { + unsafe { mainloop_api::from_raw_ptr(ffi::pa_threaded_mainloop_get_api(self.raw_mut())) } + } + + pub fn in_thread(&self) -> bool { + unsafe { ffi::pa_threaded_mainloop_in_thread(self.raw_mut()) != 0 } + } +} + +impl ::std::default::Default for ThreadedMainloop { + fn default() -> Self { + ThreadedMainloop(::std::ptr::null_mut()) + } +} + +impl ::std::ops::Drop for ThreadedMainloop { + fn drop(&mut self) { + if !self.is_null() { + unsafe { + ffi::pa_threaded_mainloop_free(self.raw_mut()); + } + } + } +} diff --git a/third_party/rust/pulse/src/util.rs b/third_party/rust/pulse/src/util.rs new file mode 100644 index 0000000000..8f18a49857 --- /dev/null +++ b/third_party/rust/pulse/src/util.rs @@ -0,0 +1,42 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use std::ffi::CStr; +use std::os::raw::c_char; +use std::ptr; + +pub trait UnwrapCStr { + fn unwrap_cstr(self) -> *const c_char; +} + +impl<'a, U> UnwrapCStr for U +where + U: Into<Option<&'a CStr>>, +{ + fn unwrap_cstr(self) -> *const c_char { + self.into().map(|o| o.as_ptr()).unwrap_or(0 as *const _) + } +} + +pub fn map_to_mut_ptr<T, U, F: FnOnce(&T) -> *mut U>(t: Option<&mut T>, f: F) -> *mut U { + match t { + Some(x) => f(x), + None => ptr::null_mut(), + } +} + +pub fn str_to_ptr(s: Option<&CStr>) -> *const c_char { + match s { + Some(x) => x.as_ptr(), + None => ptr::null(), + } +} + +pub fn to_ptr<T>(t: Option<&T>) -> *const T { + match t { + Some(x) => x as *const T, + None => ptr::null(), + } +} |