adad
This commit is contained in:
parent
0f3652a6b2
commit
9e4653fbd1
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
use audio::alsa::backend::*;
|
use audio::alsa::backend::*;
|
||||||
|
use audio::pulseaudio::*;
|
||||||
use audio::frontend::*;
|
use audio::frontend::*;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use gtk;
|
use gtk;
|
||||||
@ -52,6 +53,19 @@ pub fn new_alsa_appstate() -> AppS<AlsaBackend> {
|
|||||||
return AppS::new(prefs, audio);
|
return AppS::new(prefs, audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new application state using the `PABackend`.
|
||||||
|
pub fn new_pa_appstate() -> AppS<PABackend> {
|
||||||
|
let prefs = RefCell::new(unwrap_error!(Prefs::new(), None));
|
||||||
|
|
||||||
|
let card_name = prefs.borrow().device_prefs.card.clone();
|
||||||
|
let chan_name = prefs.borrow().device_prefs.channel.clone();
|
||||||
|
let audio = Rc::new(unwrap_error!(
|
||||||
|
PABackend::new(Some(card_name), Some(chan_name)),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
return AppS::new(prefs, audio);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T> AppS<T>
|
impl<T> AppS<T>
|
||||||
where
|
where
|
||||||
|
@ -136,8 +136,20 @@ impl AudioFrontend for AlsaBackend {
|
|||||||
return Ok(n);
|
return Ok(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn playable_chan_names(&self) -> Vec<String> {
|
fn playable_chan_names(&self, cardname: Option<String>) -> Vec<String> {
|
||||||
return get_playable_selem_names(&self.acard.borrow().mixer);
|
match cardname {
|
||||||
|
Some(name) => {
|
||||||
|
let card = try_r!(get_alsa_card_by_name(name), Vec::default());
|
||||||
|
let mixer = try_r!(get_mixer(&card), Vec::default());
|
||||||
|
|
||||||
|
return get_playable_selem_names(&mixer);
|
||||||
|
},
|
||||||
|
None => self.acard.borrow().playable_chan_names(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn playable_card_names(&self) -> Vec<String> {
|
||||||
|
return get_playable_alsa_card_names();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vol(&self) -> Result<f64> {
|
fn get_vol(&self) -> Result<f64> {
|
||||||
@ -286,29 +298,6 @@ impl AudioFrontend for AlsaBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Invokes the registered handlers.
|
|
||||||
fn invoke_handlers(
|
|
||||||
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
|
||||||
signal: AudioSignal,
|
|
||||||
user: AudioUser,
|
|
||||||
) {
|
|
||||||
debug!(
|
|
||||||
"Invoking handlers for signal {:?} by user {:?}",
|
|
||||||
signal,
|
|
||||||
user
|
|
||||||
);
|
|
||||||
if handlers.is_empty() {
|
|
||||||
debug!("No handler found");
|
|
||||||
} else {
|
|
||||||
debug!("Executing handlers")
|
|
||||||
}
|
|
||||||
for handler in handlers {
|
|
||||||
let unboxed = handler.as_ref();
|
|
||||||
unboxed(signal, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The callback for alsa events that is passed to the alsa subsystem.
|
/// The callback for alsa events that is passed to the alsa subsystem.
|
||||||
/// This is the bridge between low-level alsa events and "high-level"
|
/// This is the bridge between low-level alsa events and "high-level"
|
||||||
/// audio system signals.
|
/// audio system signals.
|
||||||
|
@ -184,6 +184,11 @@ impl AlsaCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get playable channel names of the given card.
|
||||||
|
pub fn playable_chan_names(&self) -> Vec<String> {
|
||||||
|
return get_playable_selem_names(&self.mixer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -153,8 +153,11 @@ pub trait AudioFrontend {
|
|||||||
/// Get the current card name.
|
/// Get the current card name.
|
||||||
fn card_name(&self) -> Result<String>;
|
fn card_name(&self) -> Result<String>;
|
||||||
|
|
||||||
|
/// Get the currently playable card names.
|
||||||
|
fn playable_card_names(&self) -> Vec<String>;
|
||||||
|
|
||||||
/// Get the currently playable channel names.
|
/// Get the currently playable channel names.
|
||||||
fn playable_chan_names(&self) -> Vec<String>;
|
fn playable_chan_names(&self, cardname: Option<String>) -> Vec<String>;
|
||||||
|
|
||||||
/// Get the current active channel name.
|
/// Get the current active channel name.
|
||||||
fn chan_name(&self) -> Result<String>;
|
fn chan_name(&self) -> Result<String>;
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
|
||||||
//! Pulseaudio backend subsystem.
|
//! Pulseaudio backend subsystem.
|
||||||
|
|
||||||
use audio::frontend::*;
|
use audio::frontend::*;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use libc;
|
use libc;
|
||||||
use libpulse_sys::*;
|
use libpulse_sys::*;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ffi::{CString, CStr};
|
use std::ffi::{CString};
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use support::pulseaudio::*;
|
use support::pulseaudio::*;
|
||||||
use support::audio::*;
|
use support::audio::*;
|
||||||
|
|
||||||
|
|
||||||
|
pub const PA_VOLUME_MUTED: i64 = 0x0;
|
||||||
|
pub const PA_VOLUME_NORM: i64 = 0x10000;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: get info based on index, not descr.
|
// TODO: get info based on index, not descr.
|
||||||
//
|
//
|
||||||
// TODO: how to hook pulseaudio events? port change?
|
// TODO: how to hook pulseaudio events? port change?
|
||||||
@ -28,17 +35,47 @@ pub struct Sink {
|
|||||||
pub channels: u8,
|
pub channels: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Sink {
|
||||||
|
pub fn new(sink_desc: Option<String>,
|
||||||
|
chan_name: Option<String>,
|
||||||
|
mainloop: *mut pa_threaded_mainloop,
|
||||||
|
context: *mut pa_context) -> Result<Self> {
|
||||||
|
let sink = {
|
||||||
|
match sink_desc.as_ref().map(|s| s.as_str()) {
|
||||||
|
Some("(default)") => get_first_sink(mainloop, context)?,
|
||||||
|
Some(sd) => {
|
||||||
|
let mysink = get_sink_by_desc(mainloop, context, sd);
|
||||||
|
match mysink {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => {
|
||||||
|
warn!("Could not find sink with name {}, trying others", sd);
|
||||||
|
get_first_sink(mainloop, context)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
None => get_first_sink(mainloop, context)?
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct PABackend {
|
pub struct PABackend {
|
||||||
_cannot_construct: (),
|
_cannot_construct: (),
|
||||||
m: *mut pa_threaded_mainloop,
|
m: *mut pa_threaded_mainloop,
|
||||||
c: *mut pa_context,
|
c: *mut pa_context,
|
||||||
pub sink: RefCell<Sink>,
|
pub sink: RefCell<Sink>,
|
||||||
|
pub scroll_step: Cell<u32>,
|
||||||
|
pub handlers: Handlers,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl PABackend {
|
impl PABackend {
|
||||||
pub fn new(sink_desc: Option<String>) -> Result<Self> {
|
pub fn new(sink_desc: Option<String>, chan_name: Option<String>) -> Result<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mainloop: *mut pa_threaded_mainloop = pa_threaded_mainloop_new();
|
let mainloop: *mut pa_threaded_mainloop = pa_threaded_mainloop_new();
|
||||||
|
|
||||||
@ -86,35 +123,74 @@ impl PABackend {
|
|||||||
pa_threaded_mainloop_unlock(mainloop);
|
pa_threaded_mainloop_unlock(mainloop);
|
||||||
CONTEXT_READY = false;
|
CONTEXT_READY = false;
|
||||||
|
|
||||||
let sink = {
|
let sink = Sink::new(sink_desc, chan_name, mainloop, context)?;
|
||||||
match sink_desc.as_ref().map(|s| s.as_str()) {
|
|
||||||
Some("(default)") => get_first_sink(mainloop, context)?,
|
|
||||||
Some(sd) => {
|
|
||||||
let mysink = get_sink_by_desc(mainloop, context, sd);
|
|
||||||
match mysink {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => {
|
|
||||||
warn!("Could not find sink with name {}, trying others", sd);
|
|
||||||
get_first_sink(mainloop, context)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
None => get_first_sink(mainloop, context)?
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock(mainloop);
|
||||||
|
|
||||||
|
let mut success: bool = false;
|
||||||
|
let data = &mut(mainloop, &mut success);
|
||||||
|
let o = pa_context_subscribe(context,
|
||||||
|
PA_SUBSCRIPTION_MASK_SINK,
|
||||||
|
Some(context_subscribe_cb),
|
||||||
|
data as *mut _ as *mut libc::c_void);
|
||||||
|
|
||||||
|
if o.is_null() {
|
||||||
|
pa_threaded_mainloop_unlock(mainloop);
|
||||||
|
bail!("Failed to initialize PA operation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
while pa_operation_get_state(o) == PA_OPERATION_RUNNING {
|
||||||
|
pa_threaded_mainloop_wait(mainloop);
|
||||||
|
}
|
||||||
|
pa_operation_unref(o);
|
||||||
|
pa_threaded_mainloop_unlock(mainloop);
|
||||||
|
|
||||||
|
|
||||||
|
let handlers = Handlers::new();
|
||||||
|
let cb_box = {
|
||||||
|
let h_ref: &Vec<Box<Fn(AudioSignal, AudioUser)>> = &handlers.borrow();
|
||||||
|
Box::new((mainloop, h_ref as *const Vec<Box<Fn(AudioSignal, AudioUser)>>))
|
||||||
};
|
};
|
||||||
|
{
|
||||||
|
pa_context_set_subscribe_callback(context,
|
||||||
|
Some(sub_callback),
|
||||||
|
Box::into_raw(cb_box) as *mut libc::c_void);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return Ok(PABackend {
|
return Ok(PABackend {
|
||||||
_cannot_construct: (),
|
_cannot_construct: (),
|
||||||
m: mainloop,
|
m: mainloop,
|
||||||
c: context,
|
c: context,
|
||||||
sink: RefCell::new(sink),
|
sink: RefCell::new(sink),
|
||||||
|
scroll_step: Cell::new(5),
|
||||||
|
handlers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_vol(&self) -> Result<f64> {
|
|
||||||
|
impl AudioFrontend for PABackend {
|
||||||
|
// TODO
|
||||||
|
fn switch_card(
|
||||||
|
&self,
|
||||||
|
card_name: Option<String>,
|
||||||
|
elem_name: Option<String>,
|
||||||
|
user: AudioUser,
|
||||||
|
) -> Result<()> {
|
||||||
|
{
|
||||||
|
let mut ac = self.sink.borrow_mut();
|
||||||
|
*ac = Sink::new(card_name, elem_name, self.m, self.c)?;
|
||||||
|
}
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn get_vol(&self) -> Result<f64> {
|
||||||
|
|
||||||
let mut vol: u32 = 0;
|
let mut vol: u32 = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -140,19 +216,21 @@ impl PABackend {
|
|||||||
let _ = CString::from_raw(sink_name);
|
let _ = CString::from_raw(sink_name);
|
||||||
|
|
||||||
}
|
}
|
||||||
unsafe {
|
|
||||||
return Ok(pa_sw_volume_to_linear(vol) * 100.0);
|
return vol_to_percent(vol as i64, (PA_VOLUME_MUTED,
|
||||||
}
|
PA_VOLUME_NORM))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_vol(&self, new_vol: f64, dir: VolDir) -> Result<()> {
|
|
||||||
|
fn set_vol(&self, new_vol: f64, user: AudioUser, dir: VolDir, auto_unmute: bool) -> Result<()> {
|
||||||
let mut res: Result<()> = Err("No value".into());
|
let mut res: Result<()> = Err("No value".into());
|
||||||
|
let new_vol = percent_to_vol(new_vol, (PA_VOLUME_MUTED,
|
||||||
|
PA_VOLUME_NORM), dir)?;
|
||||||
unsafe {
|
unsafe {
|
||||||
pa_threaded_mainloop_lock(self.m);
|
pa_threaded_mainloop_lock(self.m);
|
||||||
let data = &mut(self, &mut res);
|
let data = &mut(self, &mut res);
|
||||||
let sink_name = CString::new(self.sink.borrow().name.clone()).unwrap().into_raw();
|
let sink_name = CString::new(self.sink.borrow().name.clone()).unwrap().into_raw();
|
||||||
|
|
||||||
let new_vol = pa_sw_volume_from_linear(new_vol / 100.0);
|
|
||||||
let mut vol_arr: [u32; 32] = [0; 32];
|
let mut vol_arr: [u32; 32] = [0; 32];
|
||||||
for c in 0..(self.sink.borrow().channels) {
|
for c in 0..(self.sink.borrow().channels) {
|
||||||
vol_arr[c as usize] = new_vol as u32;
|
vol_arr[c as usize] = new_vol as u32;
|
||||||
@ -185,11 +263,44 @@ impl PABackend {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_mute(&self) -> bool {
|
|
||||||
|
fn vol_level(&self) -> VolLevel {
|
||||||
|
let muted = self.get_mute().unwrap_or(false);
|
||||||
|
if muted {
|
||||||
|
return VolLevel::Muted;
|
||||||
|
}
|
||||||
|
let cur_vol = try_r!(self.get_vol(), VolLevel::Muted);
|
||||||
|
match cur_vol {
|
||||||
|
0. => return VolLevel::Off,
|
||||||
|
0.0...33.0 => return VolLevel::Low,
|
||||||
|
0.0...66.0 => return VolLevel::Medium,
|
||||||
|
0.0...100.0 => return VolLevel::High,
|
||||||
|
_ => return VolLevel::Off,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn increase_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()> {
|
||||||
|
let old_vol = self.get_vol()?;
|
||||||
|
let new_vol = old_vol + (self.scroll_step.get() as f64);
|
||||||
|
|
||||||
|
return self.set_vol(new_vol, user, VolDir::Up, auto_unmute);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrease_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()> {
|
||||||
|
let old_vol = self.get_vol()?;
|
||||||
|
let new_vol = old_vol - (self.scroll_step.get() as f64);
|
||||||
|
|
||||||
|
return self.set_vol(new_vol, user, VolDir::Down, auto_unmute);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn has_mute(&self) -> bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_mute(&self) -> Result<bool> {
|
|
||||||
|
fn get_mute(&self) -> Result<bool> {
|
||||||
let mut mute: bool = false;
|
let mut mute: bool = false;
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
||||||
@ -216,7 +327,8 @@ impl PABackend {
|
|||||||
return Ok(mute);
|
return Ok(mute);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mute(&self, mute: bool) -> Result<()> {
|
|
||||||
|
fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
|
||||||
let mut res: Result<()> = Err("No value".into());
|
let mut res: Result<()> = Err("No value".into());
|
||||||
unsafe {
|
unsafe {
|
||||||
pa_threaded_mainloop_lock(self.m);
|
pa_threaded_mainloop_lock(self.m);
|
||||||
@ -243,6 +355,45 @@ impl PABackend {
|
|||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_mute(&self, user: AudioUser) -> Result<()> {
|
||||||
|
let muted = self.get_mute()?;
|
||||||
|
return self.set_mute(!muted, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
fn connect_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
|
||||||
|
self.handlers.add_handler(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: name or desc?
|
||||||
|
fn card_name(&self) -> Result<String> {
|
||||||
|
return Ok(self.sink.borrow().description.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn playable_card_names(&self) -> Vec<String> {
|
||||||
|
let sinks = try_r!(get_sinks(self.m, self.c), vec![]);
|
||||||
|
return sinks.iter().map(|s| s.description.clone()).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
fn playable_chan_names(&self, cardname: Option<String>) -> Vec<String> {
|
||||||
|
return vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
fn chan_name(&self) -> Result<String> {
|
||||||
|
return Ok(String::from("Blah"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_scroll_step(&self, scroll_step: u32) {
|
||||||
|
self.scroll_step.set(scroll_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_scroll_step(&self) -> u32 {
|
||||||
|
return self.scroll_step.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -284,13 +435,14 @@ unsafe extern "C" fn context_state_cb(
|
|||||||
|
|
||||||
// TODO: Better error handling.
|
// TODO: Better error handling.
|
||||||
unsafe extern "C" fn get_sink_vol(
|
unsafe extern "C" fn get_sink_vol(
|
||||||
ctx: *mut pa_context,
|
_: *mut pa_context,
|
||||||
i: *const pa_sink_info,
|
i: *const pa_sink_info,
|
||||||
eol: i32,
|
_: i32,
|
||||||
data: *mut libc::c_void) {
|
data: *mut libc::c_void) {
|
||||||
let (_self, res) = *(data as *mut (*mut PABackend,
|
let (_self, res) = *(data as *mut (*mut PABackend,
|
||||||
*mut u32));
|
*mut u32));
|
||||||
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
||||||
|
assert!(!res.is_null(), "res is null");
|
||||||
|
|
||||||
if i.is_null() {
|
if i.is_null() {
|
||||||
return
|
return
|
||||||
@ -303,13 +455,14 @@ unsafe extern "C" fn get_sink_vol(
|
|||||||
|
|
||||||
// TODO: Better error handling.
|
// TODO: Better error handling.
|
||||||
unsafe extern "C" fn get_sink_mute(
|
unsafe extern "C" fn get_sink_mute(
|
||||||
ctx: *mut pa_context,
|
_: *mut pa_context,
|
||||||
i: *const pa_sink_info,
|
i: *const pa_sink_info,
|
||||||
eol: i32,
|
_: i32,
|
||||||
data: *mut libc::c_void) {
|
data: *mut libc::c_void) {
|
||||||
let (_self, res) = *(data as *mut (*mut PABackend,
|
let (_self, res) = *(data as *mut (*mut PABackend,
|
||||||
*mut bool));
|
*mut bool));
|
||||||
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
||||||
|
assert!(!res.is_null(), "res is null");
|
||||||
|
|
||||||
if i.is_null() {
|
if i.is_null() {
|
||||||
return
|
return
|
||||||
@ -322,12 +475,13 @@ unsafe extern "C" fn get_sink_mute(
|
|||||||
|
|
||||||
// TODO: Missing error handling.
|
// TODO: Missing error handling.
|
||||||
unsafe extern "C" fn set_sink_vol(
|
unsafe extern "C" fn set_sink_vol(
|
||||||
ctx: *mut pa_context,
|
_: *mut pa_context,
|
||||||
success: i32,
|
success: i32,
|
||||||
data: *mut libc::c_void) {
|
data: *mut libc::c_void) {
|
||||||
let (_self, res) = *(data as *mut (*mut PABackend,
|
let (_self, res) = *(data as *mut (*mut PABackend,
|
||||||
*mut Result<()>));
|
*mut Result<()>));
|
||||||
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
||||||
|
assert!(!res.is_null(), "res is null");
|
||||||
|
|
||||||
if success > 0 {
|
if success > 0 {
|
||||||
*res = Ok(());
|
*res = Ok(());
|
||||||
@ -342,12 +496,13 @@ unsafe extern "C" fn set_sink_vol(
|
|||||||
// TODO: Missing error handling.
|
// TODO: Missing error handling.
|
||||||
// TODO: same as 'set_sink_vol'
|
// TODO: same as 'set_sink_vol'
|
||||||
unsafe extern "C" fn set_sink_mute(
|
unsafe extern "C" fn set_sink_mute(
|
||||||
ctx: *mut pa_context,
|
_: *mut pa_context,
|
||||||
success: i32,
|
success: i32,
|
||||||
data: *mut libc::c_void) {
|
data: *mut libc::c_void) {
|
||||||
let (_self, res) = *(data as *mut (*mut PABackend,
|
let (_self, res) = *(data as *mut (*mut PABackend,
|
||||||
*mut Result<()>));
|
*mut Result<()>));
|
||||||
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
assert!(!(*_self).m.is_null(), "Mainloop is null");
|
||||||
|
assert!(!res.is_null(), "res is null");
|
||||||
|
|
||||||
if success > 0 {
|
if success > 0 {
|
||||||
*res = Ok(());
|
*res = Ok(());
|
||||||
@ -357,3 +512,51 @@ unsafe extern "C" fn set_sink_mute(
|
|||||||
|
|
||||||
pa_threaded_mainloop_signal((*_self).m, 0);
|
pa_threaded_mainloop_signal((*_self).m, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsafe extern "C" fn context_subscribe_cb(c: *mut pa_context,
|
||||||
|
success: i32,
|
||||||
|
data: *mut libc::c_void) {
|
||||||
|
let (mainloop, res) = *(data as *mut (*mut pa_threaded_mainloop,
|
||||||
|
*mut bool));
|
||||||
|
|
||||||
|
assert!(!mainloop.is_null(), "Mainloop is null");
|
||||||
|
assert!(!res.is_null(), "res is null");
|
||||||
|
|
||||||
|
if success > 0 {
|
||||||
|
*res = true;
|
||||||
|
} else {
|
||||||
|
*res = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pa_threaded_mainloop_signal(mainloop, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsafe extern "C" fn sub_callback(c: *mut pa_context,
|
||||||
|
t: u32,
|
||||||
|
idx: u32,
|
||||||
|
data: *mut libc::c_void) {
|
||||||
|
|
||||||
|
let (mainloop, p_handlers) = *(data as *mut (*mut pa_threaded_mainloop,
|
||||||
|
*mut Vec<Box<Fn(AudioSignal, AudioUser)>>));
|
||||||
|
|
||||||
|
assert!(!mainloop.is_null(), "Mainloop is null");
|
||||||
|
assert!(!p_handlers.is_null(), "Handlers are null");
|
||||||
|
|
||||||
|
let handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>> = &*p_handlers;
|
||||||
|
|
||||||
|
if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_SINK {
|
||||||
|
if (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE {
|
||||||
|
// invoke_handlers(
|
||||||
|
// handlers,
|
||||||
|
// AudioSignal::ValuesChanged,
|
||||||
|
// AudioUser::Unknown,
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
19
src/bin.rs
19
src/bin.rs
@ -22,6 +22,7 @@ use support::audio::VolDir;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
|
|
||||||
@ -73,22 +74,22 @@ fn main() {
|
|||||||
.unwrap_or_else(|e|{panic!("Logger initialization failed with {}",e)});
|
.unwrap_or_else(|e|{panic!("Logger initialization failed with {}",e)});
|
||||||
|
|
||||||
|
|
||||||
let pa = PABackend::new(Some(String::from("Built-in Audio Analog Stereo"))).unwrap();
|
// let pa = PABackend::new(Some(String::from("Built-in Audio Analog Stereo"))).unwrap();
|
||||||
println!("Sink: {:?}", pa.sink);
|
// println!("Sink: {:?}", pa.sink);
|
||||||
println!("Volume before: {:?}", pa.get_vol());
|
// println!("Volume before: {:?}", pa.get_vol());
|
||||||
pa.set_vol(80.0, VolDir::Up);
|
// pa.set_vol(80.0, VolDir::Up);
|
||||||
println!("Volume after: {:?}", pa.get_vol());
|
// println!("Volume after: {:?}", pa.get_vol());
|
||||||
println!("Mute before: {:?}", pa.get_mute());
|
// println!("Mute before: {:?}", pa.get_mute());
|
||||||
println!("PA_VOLUME_NORM: {:?}", PA_VOLUME_NORM);
|
// println!("PA_VOLUME_NORM: {:?}", PA_VOLUME_NORM);
|
||||||
// pa.set_mute(true);
|
// pa.set_mute(true);
|
||||||
// println!("Mute after: {:?}", pa.get_mute());
|
// println!("Mute after: {:?}", pa.get_mute());
|
||||||
|
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
gtk::init()
|
gtk::init()
|
||||||
.unwrap_or_else(|e| panic!("Gtk initialization failed with {}", e));
|
.unwrap_or_else(|e| panic!("Gtk initialization failed with {}", e));
|
||||||
|
|
||||||
let apps = Rc::new(new_alsa_appstate());
|
let apps = Rc::new(new_pa_appstate());
|
||||||
|
|
||||||
ui::entry::init(apps);
|
ui::entry::init(apps);
|
||||||
|
|
||||||
|
62
src/notif.rs
62
src/notif.rs
@ -144,37 +144,37 @@ where
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
/* connect handler */
|
/* connect handler */
|
||||||
let apps = appstate.clone();
|
// let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| {
|
// appstate.audio.connect_handler(Box::new(move |s, u| {
|
||||||
let notif = &apps.notif;
|
// let notif = &apps.notif;
|
||||||
if notif.is_none() || !notif.as_ref().unwrap().enabled.get() {
|
// if notif.is_none() || !notif.as_ref().unwrap().enabled.get() {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
let notif = notif.as_ref().unwrap();
|
// let notif = notif.as_ref().unwrap();
|
||||||
match (s,
|
// match (s,
|
||||||
u,
|
// u,
|
||||||
(notif.from_popup.get(),
|
// (notif.from_popup.get(),
|
||||||
notif.from_tray.get(),
|
// notif.from_tray.get(),
|
||||||
notif.from_external.get(),
|
// notif.from_external.get(),
|
||||||
notif.from_hotkeys.get())) {
|
// notif.from_hotkeys.get())) {
|
||||||
(AudioSignal::NoCard, _, _) => try_w!(notif.show_text_notif("No sound card", "No playable soundcard found")),
|
// (AudioSignal::NoCard, _, _) => try_w!(notif.show_text_notif("No sound card", "No playable soundcard found")),
|
||||||
(AudioSignal::CardDisconnected, _, _) => try_w!(notif.show_text_notif("Soundcard disconnected", "Soundcard has been disconnected, reloading sound system...")),
|
// (AudioSignal::CardDisconnected, _, _) => try_w!(notif.show_text_notif("Soundcard disconnected", "Soundcard has been disconnected, reloading sound system...")),
|
||||||
(AudioSignal::CardError, _, _) => (),
|
// (AudioSignal::CardError, _, _) => (),
|
||||||
(AudioSignal::ValuesChanged,
|
// (AudioSignal::ValuesChanged,
|
||||||
AudioUser::TrayIcon,
|
// AudioUser::TrayIcon,
|
||||||
(_, true, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
// (_, true, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
||||||
(AudioSignal::ValuesChanged,
|
// (AudioSignal::ValuesChanged,
|
||||||
AudioUser::Popup,
|
// AudioUser::Popup,
|
||||||
(true, _, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
// (true, _, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
||||||
(AudioSignal::ValuesChanged,
|
// (AudioSignal::ValuesChanged,
|
||||||
AudioUser::Unknown,
|
// AudioUser::Unknown,
|
||||||
(_, _, true, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
// (_, _, true, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
||||||
(AudioSignal::ValuesChanged,
|
// (AudioSignal::ValuesChanged,
|
||||||
AudioUser::Hotkeys,
|
// AudioUser::Hotkeys,
|
||||||
(_, _, _, true)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
// (_, _, _, true)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
|
||||||
_ => (),
|
// _ => (),
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ use audio::frontend::*;
|
|||||||
use errors::*;
|
use errors::*;
|
||||||
use prefs::*;
|
use prefs::*;
|
||||||
|
|
||||||
// TODO: rm alsa
|
|
||||||
use support::alsa::*;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -100,16 +98,25 @@ pub fn percent_to_vol(vol: f64, range: (i64, i64), dir: VolDir) -> Result<i64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Get all playable card names.
|
/// Invokes the registered handlers.
|
||||||
pub fn get_playable_card_names() -> Vec<String> {
|
pub fn invoke_handlers(
|
||||||
return get_playable_alsa_card_names();
|
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
||||||
}
|
signal: AudioSignal,
|
||||||
|
user: AudioUser,
|
||||||
|
) {
|
||||||
/// Get all playable channel names.
|
debug!(
|
||||||
pub fn get_playable_chan_names(card_name: String) -> Vec<String> {
|
"Invoking handlers for signal {:?} by user {:?}",
|
||||||
let card = try_r!(get_alsa_card_by_name(card_name), Vec::default());
|
signal,
|
||||||
let mixer = try_r!(get_mixer(&card), Vec::default());
|
user
|
||||||
|
);
|
||||||
return get_playable_selem_names(&mixer);
|
if handlers.is_empty() {
|
||||||
|
debug!("No handler found");
|
||||||
|
} else {
|
||||||
|
debug!("Executing handlers")
|
||||||
|
}
|
||||||
|
for handler in handlers {
|
||||||
|
debug!("Handler executing");
|
||||||
|
let unboxed = handler.as_ref();
|
||||||
|
unboxed(signal, user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,31 +63,31 @@ where
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
/* "global" audio signal handler */
|
/* "global" audio signal handler */
|
||||||
let apps = appstate.clone();
|
// let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(
|
// appstate.audio.connect_handler(
|
||||||
Box::new(move |s, u| match (s, u) {
|
// Box::new(move |s, u| match (s, u) {
|
||||||
(AudioSignal::CardDisconnected, _) => {
|
// (AudioSignal::CardDisconnected, _) => {
|
||||||
try_w!(audio_reload(
|
// try_w!(audio_reload(
|
||||||
apps.audio.as_ref(),
|
// apps.audio.as_ref(),
|
||||||
&apps.prefs.borrow(),
|
// &apps.prefs.borrow(),
|
||||||
AudioUser::Unknown,
|
// AudioUser::Unknown,
|
||||||
));
|
// ));
|
||||||
}
|
// }
|
||||||
(AudioSignal::CardError, _) => {
|
// (AudioSignal::CardError, _) => {
|
||||||
if run_audio_error_dialog(
|
// if run_audio_error_dialog(
|
||||||
&apps.gui.popup_menu.menu_window,
|
// &apps.gui.popup_menu.menu_window,
|
||||||
) == (GTK_RESPONSE_YES as i32)
|
// ) == (GTK_RESPONSE_YES as i32)
|
||||||
{
|
// {
|
||||||
try_w!(audio_reload(
|
// try_w!(audio_reload(
|
||||||
apps.audio.as_ref(),
|
// apps.audio.as_ref(),
|
||||||
&apps.prefs.borrow(),
|
// &apps.prefs.borrow(),
|
||||||
AudioUser::Unknown,
|
// AudioUser::Unknown,
|
||||||
));
|
// ));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
_ => (),
|
// _ => (),
|
||||||
}),
|
// }),
|
||||||
);
|
// );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,16 +48,16 @@ where
|
|||||||
{
|
{
|
||||||
/* audio.connect_handler */
|
/* audio.connect_handler */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
// let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| {
|
// appstate.audio.connect_handler(Box::new(move |s, u| {
|
||||||
/* skip if window is hidden */
|
// /* skip if window is hidden */
|
||||||
if !apps.gui.popup_menu.menu.get_visible() {
|
// if !apps.gui.popup_menu.menu.get_visible() {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
match (s, u) {
|
// match (s, u) {
|
||||||
(_, _) => set_mute_check(&apps),
|
// (_, _) => set_mute_check(&apps),
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,31 +125,31 @@ where
|
|||||||
{
|
{
|
||||||
/* audio.connect_handler */
|
/* audio.connect_handler */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
// let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| {
|
// appstate.audio.connect_handler(Box::new(move |s, u| {
|
||||||
/* skip if window is hidden */
|
// /* skip if window is hidden */
|
||||||
if !apps.gui.popup_window.popup_window.get_visible() {
|
// if !apps.gui.popup_window.popup_window.get_visible() {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
match (s, u) {
|
// match (s, u) {
|
||||||
/* Update only mute check here
|
// /* Update only mute check here
|
||||||
* If the user changes the volume through the popup window,
|
// * If the user changes the volume through the popup window,
|
||||||
* we MUST NOT update the slider value, it's been done already.
|
// * we MUST NOT update the slider value, it's been done already.
|
||||||
* It means that, as long as the popup window is visible,
|
// * It means that, as long as the popup window is visible,
|
||||||
* the slider value reflects the value set by user,
|
// * the slider value reflects the value set by user,
|
||||||
* and not the real value reported by the audio system.
|
// * and not the real value reported by the audio system.
|
||||||
*/
|
// */
|
||||||
(_, AudioUser::Popup) => {
|
// (_, AudioUser::Popup) => {
|
||||||
apps.gui.popup_window.update_mute_check(
|
// apps.gui.popup_window.update_mute_check(
|
||||||
apps.audio.as_ref(),
|
// apps.audio.as_ref(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
/* external change, safe to update slider too */
|
// /* external change, safe to update slider too */
|
||||||
(_, _) => {
|
// (_, _) => {
|
||||||
try_w!(apps.gui.popup_window.update(apps.audio.as_ref()));
|
// try_w!(apps.gui.popup_window.update(apps.audio.as_ref()));
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mute_check.connect_toggled */
|
/* mute_check.connect_toggled */
|
||||||
|
@ -13,7 +13,6 @@ use prefs::*;
|
|||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use support::audio::*;
|
|
||||||
use ui::hotkey_dialog::HotkeyDialog;
|
use ui::hotkey_dialog::HotkeyDialog;
|
||||||
|
|
||||||
|
|
||||||
@ -434,21 +433,21 @@ where
|
|||||||
T: AudioFrontend + 'static,
|
T: AudioFrontend + 'static,
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| {
|
// appstate.audio.connect_handler(Box::new(move |s, u| {
|
||||||
/* skip if prefs window is not present */
|
// /* skip if prefs window is not present */
|
||||||
if apps.gui.prefs_dialog.borrow().is_none() {
|
// if apps.gui.prefs_dialog.borrow().is_none() {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
match (s, u) {
|
// match (s, u) {
|
||||||
(AudioSignal::CardInitialized, _) => (),
|
// (AudioSignal::CardInitialized, _) => (),
|
||||||
(AudioSignal::CardCleanedUp, _) => {
|
// (AudioSignal::CardCleanedUp, _) => {
|
||||||
fill_card_combo(&apps);
|
// fill_card_combo(&apps);
|
||||||
fill_chan_combo(&apps, None);
|
// fill_chan_combo(&apps, None);
|
||||||
}
|
// }
|
||||||
_ => (),
|
// _ => (),
|
||||||
}
|
// }
|
||||||
}));
|
// }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -586,7 +585,7 @@ where
|
|||||||
/* set card combo */
|
/* set card combo */
|
||||||
let cur_card_name =
|
let cur_card_name =
|
||||||
try_w!(audio.card_name(), "Can't get current card name!");
|
try_w!(audio.card_name(), "Can't get current card name!");
|
||||||
let available_card_names = get_playable_card_names();
|
let available_card_names = audio.playable_card_names();
|
||||||
|
|
||||||
/* set_active_id doesn't work, so save the index */
|
/* set_active_id doesn't work, so save the index */
|
||||||
let mut c_index: i32 = -1;
|
let mut c_index: i32 = -1;
|
||||||
@ -613,10 +612,7 @@ where
|
|||||||
chan_combo.remove_all();
|
chan_combo.remove_all();
|
||||||
|
|
||||||
let audio = &appstate.audio;
|
let audio = &appstate.audio;
|
||||||
let available_chan_names = match cardname {
|
let available_chan_names = audio.playable_chan_names(cardname);
|
||||||
Some(name) => get_playable_chan_names(name),
|
|
||||||
None => audio.playable_chan_names(),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* set chan combo */
|
/* set chan combo */
|
||||||
let cur_chan_name = try_w!(audio.chan_name());
|
let cur_chan_name = try_w!(audio.chan_name());
|
||||||
|
@ -422,11 +422,14 @@ where
|
|||||||
appstate.audio.connect_handler(
|
appstate.audio.connect_handler(
|
||||||
Box::new(move |s, u| match (s, u) {
|
Box::new(move |s, u| match (s, u) {
|
||||||
(_, _) => {
|
(_, _) => {
|
||||||
apps.gui.tray_icon.update_tooltip(apps.audio.as_ref());
|
// apps.gui.tray_icon.update_tooltip(apps.audio.as_ref());
|
||||||
try_w!(apps.gui.tray_icon.update_vol_meter(
|
// try_w!(apps.gui.tray_icon.update_vol_meter(
|
||||||
try_w!(apps.audio.get_vol()),
|
// try_w!(apps.audio.get_vol()),
|
||||||
apps.audio.vol_level(),
|
// apps.audio.vol_level(),
|
||||||
));
|
// ));
|
||||||
|
let vol = apps.audio.get_vol();
|
||||||
|
println!("Vol: {:?}", vol)
|
||||||
|
// println!("Gaga");
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user