This commit is contained in:
Julian Ospald 2017-06-29 23:35:39 +02:00
parent fb98506fe3
commit de9afc9469
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
8 changed files with 116 additions and 102 deletions

View File

@ -9,4 +9,3 @@ pub struct AppS {
pub builder_popup: gtk::Builder, pub builder_popup: gtk::Builder,
} }

View File

@ -3,12 +3,14 @@ use alsa::mixer::{Mixer, Selem, SelemId};
use alsa::poll::PollDescriptors; use alsa::poll::PollDescriptors;
use alsa_sys; use alsa_sys;
use errors::*; use errors::*;
use glib;
use glib_sys; use glib_sys;
use libc::c_uint; use libc::c_uint;
use libc::pollfd; use libc::pollfd;
use libc::size_t; use libc::size_t;
use myalsa::*; use myalsa::*;
use std::mem; use std::mem;
use std::cell::RefCell;
use std::ptr; use std::ptr;
use std::u8; use std::u8;
@ -21,15 +23,15 @@ pub struct AlsaCard {
pub mixer: Mixer, pub mixer: Mixer,
pub selem_id: SelemId, pub selem_id: SelemId,
pub watch_ids: Vec<u32>, pub watch_ids: Vec<u32>,
last_action_timestamp: RefCell<i64>,
} }
/* TODO: AlsaCard cleanup */ /* TODO: AlsaCard cleanup */
impl AlsaCard { impl AlsaCard {
pub fn new( pub fn new(card_name: Option<String>,
card_name: Option<String>, elem_name: Option<String>)
elem_name: Option<String>, -> Result<AlsaCard> {
) -> Result<AlsaCard> {
let card = { let card = {
match card_name { match card_name {
Some(name) => get_alsa_card_by_name(name)?, Some(name) => get_alsa_card_by_name(name)?,
@ -37,31 +39,33 @@ impl AlsaCard {
} }
}; };
let mixer = get_mixer(&card)?; let mixer = get_mixer(&card)?;
let selem_id = get_selem_by_name( let selem_id =
&mixer, get_selem_by_name(&mixer,
elem_name.unwrap_or(String::from("Master")), elem_name.unwrap_or(String::from("Master")))
).unwrap() .unwrap()
.get_id(); .get_id();
let vec_pollfd = PollDescriptors::get(&mixer)?; let vec_pollfd = PollDescriptors::get(&mixer)?;
/* TODO: callback is registered here, which must be unregistered /* TODO: callback is registered here, which must be unregistered
* when the mixer is destroyed!! */ * when the mixer is destroyed!!
* poll descriptors must be unwatched too */
let watch_ids = watch_poll_descriptors(vec_pollfd, &mixer); let watch_ids = watch_poll_descriptors(vec_pollfd, &mixer);
return Ok(AlsaCard { return Ok(AlsaCard {
_cannot_construct: (), _cannot_construct: (),
card: card, card: card,
mixer: mixer, mixer: mixer,
selem_id: selem_id, selem_id: selem_id,
watch_ids: watch_ids, watch_ids: watch_ids,
}); last_action_timestamp: RefCell::new(0),
});
} }
pub fn selem(&self) -> Selem { pub fn selem(&self) -> Selem {
return get_selems(&self.mixer) return get_selems(&self.mixer)
.nth(self.selem_id.get_index() as usize) .nth(self.selem_id.get_index() as usize)
.unwrap(); .unwrap();
} }
@ -70,7 +74,10 @@ impl AlsaCard {
} }
pub fn set_vol(&self, new_vol: f64) -> Result<()> { pub fn set_vol(&self, new_vol: f64, user: AudioUser) -> Result<()> {
let mut rc = self.last_action_timestamp.borrow_mut();
*rc = glib::get_real_time();
// TODO invoke handlers, make use of user
return set_vol(&self.selem(), new_vol); return set_vol(&self.selem(), new_vol);
} }
@ -85,9 +92,31 @@ impl AlsaCard {
} }
pub fn set_mute(&self, mute: bool) -> Result<()> { pub fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
let mut rc = self.last_action_timestamp.borrow_mut();
*rc = glib::get_real_time();
// TODO invoke handlers, make use of user
return set_mute(&self.selem(), mute); return set_mute(&self.selem(), mute);
} }
fn on_alsa_event(&self, alsa_event: AlsaEvent) {
let last: i64 = *self.last_action_timestamp.borrow();
let now: i64 = glib::get_monotonic_time();
let delay = now - last;
if delay < 1000000 {
return;
}
/* external change */
match alsa_event {
// TODO: invoke handlers with AudioUserUnknown
AlsaEvent::AlsaCardError => println!("AlsaCardError"),
AlsaEvent::AlsaCardDiconnected => println!("AlsaCardDiconnected"),
AlsaEvent::AlsaCardValuesChanged => println!("AlsaCardValuesChanged"),
}
}
} }
@ -108,15 +137,17 @@ enum AudioSignal {
AudioValuesChanged, AudioValuesChanged,
} }
enum AlsaEvent {
AlsaCardError,
AlsaCardDiconnected,
AlsaCardValuesChanged,
}
fn watch_poll_descriptors(
polls: Vec<pollfd>, fn watch_poll_descriptors(polls: Vec<pollfd>, mixer: &Mixer) -> Vec<c_uint> {
mixer: &Mixer,
) -> Vec<c_uint> {
let mut watch_ids: Vec<c_uint> = vec![]; let mut watch_ids: Vec<c_uint> = vec![];
let mixer_ptr = unsafe { let mixer_ptr =
mem::transmute::<&Mixer, &*mut alsa_sys::snd_mixer_t>(mixer) unsafe { mem::transmute::<&Mixer, &*mut alsa_sys::snd_mixer_t>(mixer) };
};
for poll in polls { for poll in polls {
unsafe { unsafe {
let gioc: *mut glib_sys::GIOChannel = let gioc: *mut glib_sys::GIOChannel =
@ -136,11 +167,10 @@ fn watch_poll_descriptors(
} }
extern fn watch_cb( extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
chan: *mut glib_sys::GIOChannel, cond: glib_sys::GIOCondition,
cond: glib_sys::GIOCondition, data: glib_sys::gpointer)
data: glib_sys::gpointer, -> glib_sys::gboolean {
) -> glib_sys::gboolean {
let mixer = data as *mut alsa_sys::snd_mixer_t; let mixer = data as *mut alsa_sys::snd_mixer_t;
@ -156,21 +186,20 @@ extern fn watch_cb(
let mut buf: Vec<u8> = vec![0; 256]; let mut buf: Vec<u8> = vec![0; 256];
while sread > 0 { while sread > 0 {
let stat: glib_sys::GIOStatus = unsafe { let stat: glib_sys::GIOStatus =
glib_sys::g_io_channel_read_chars( unsafe {
chan, glib_sys::g_io_channel_read_chars(chan,
buf.as_mut_ptr() as *mut u8, buf.as_mut_ptr() as *mut u8,
256, 256,
&mut sread as *mut size_t, &mut sread as *mut size_t,
ptr::null_mut(), ptr::null_mut())
) };
};
match stat { match stat {
glib_sys::G_IO_STATUS_AGAIN => { glib_sys::G_IO_STATUS_AGAIN => {
println!("G_IO_STATUS_AGAIN"); println!("G_IO_STATUS_AGAIN");
continue continue;
}, }
glib_sys::G_IO_STATUS_NORMAL => println!("G_IO_STATUS_NORMAL"), glib_sys::G_IO_STATUS_NORMAL => println!("G_IO_STATUS_NORMAL"),
glib_sys::G_IO_STATUS_ERROR => println!("G_IO_STATUS_ERROR"), glib_sys::G_IO_STATUS_ERROR => println!("G_IO_STATUS_ERROR"),
glib_sys::G_IO_STATUS_EOF => println!("G_IO_STATUS_EOF"), glib_sys::G_IO_STATUS_EOF => println!("G_IO_STATUS_EOF"),
@ -182,4 +211,3 @@ extern fn watch_cb(
return true as glib_sys::gboolean; return true as glib_sys::gboolean;
} }

View File

@ -79,4 +79,3 @@ macro_rules! try_r {
}, },
}); });
} }

View File

@ -13,6 +13,7 @@ extern crate alsa_sys;
extern crate ffi; extern crate ffi;
extern crate gdk; extern crate gdk;
extern crate gdk_sys; extern crate gdk_sys;
extern crate glib;
extern crate glib_sys; extern crate glib_sys;
extern crate gtk; extern crate gtk;
extern crate gtk_sys; extern crate gtk_sys;
@ -41,14 +42,12 @@ fn main() {
let ref apps = AppS { let ref apps = AppS {
status_icon: gtk::StatusIcon::new_from_icon_name("pnmixer"), status_icon: gtk::StatusIcon::new_from_icon_name("pnmixer"),
builder_popup: gtk::Builder::new_from_string( builder_popup: gtk::Builder::new_from_string(include_str!("../data/ui/popup-window-vertical.glade")),
include_str!("../data/ui/popup-window-vertical.glade"),
),
}; };
let acard = Rc::new(RefCell::new( let acard = Rc::new(RefCell::new(AlsaCard::new(None,
AlsaCard::new(None, Some(String::from("Master"))).unwrap(), Some(String::from("Master")))
)); .unwrap()));
flexi_logger::LogOptions::new() flexi_logger::LogOptions::new()
.log_to_file(false) .log_to_file(false)

View File

@ -56,7 +56,9 @@ pub fn get_selems(mixer: &Mixer) -> Map<alsa::mixer::Iter, fn(Elem) -> Selem> {
pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> { pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
for selem in get_selems(mixer) { for selem in get_selems(mixer) {
let n = selem.get_id().get_name().map(|y| String::from(y))?; let n = selem.get_id()
.get_name()
.map(|y| String::from(y))?;
if n == name { if n == name {
return Ok(selem); return Ok(selem);
@ -97,9 +99,7 @@ pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> {
} }
let range = selem.get_playback_volume_range(); let range = selem.get_playback_volume_range();
selem.set_playback_volume_all( selem.set_playback_volume_all(percent_to_vol(new_vol, range))?;
percent_to_vol(new_vol, range),
)?;
return Ok(()); return Ok(());
} }
@ -121,4 +121,3 @@ pub fn set_mute(selem: &Selem, mute: bool) -> Result<()> {
let _ = selem.set_playback_switch_all(!mute as i32)?; let _ = selem.set_playback_switch_all(!mute as i32)?;
return Ok(()); return Ok(());
} }

View File

@ -12,4 +12,3 @@ pub fn init<'a>(appstate: &'a AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
init_tray_icon(&appstate); init_tray_icon(&appstate);
init_popup_window(&appstate, rc_acard); init_popup_window(&appstate, rc_acard);
} }

View File

@ -1,5 +1,6 @@
use app_state::*; use app_state::*;
use audio::AlsaCard; use audio::AlsaCard;
use audio::AudioUser::*;
use errors::*; use errors::*;
use gdk::DeviceExt; use gdk::DeviceExt;
use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK}; use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK};
@ -43,18 +44,18 @@ pub fn init_popup_window(appstate: &AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
/* vol_scale_adj.connect_value_changed */ /* vol_scale_adj.connect_value_changed */
{ {
let vol_scale_adj: Rc<gtk::Adjustment> = let vol_scale_adj: Rc<gtk::Adjustment> =
Rc::new( Rc::new(appstate.builder_popup
appstate.builder_popup.get_object("vol_scale_adj").unwrap(), .get_object("vol_scale_adj")
); .unwrap());
let card = rc_acard.clone(); let card = rc_acard.clone();
let vol_scale = vol_scale_adj.clone(); let vol_scale = vol_scale_adj.clone();
vol_scale_adj.connect_value_changed(move |_| { vol_scale_adj.connect_value_changed(move |_| {
let acard = card.borrow(); let acard = card.borrow();
let val = vol_scale.get_value(); let val = vol_scale.get_value();
try_w!(acard.set_vol(val)); try_w!(acard.set_vol(val, AudioUserPopup));
}); });
} }
/* mute_check.connect_toggled */ /* mute_check.connect_toggled */
@ -64,11 +65,11 @@ pub fn init_popup_window(appstate: &AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
let card = rc_acard.clone(); let card = rc_acard.clone();
mute_check.connect_toggled(move |_| { mute_check.connect_toggled(move |_| {
let acard = card.borrow(); let acard = card.borrow();
let muted = try_w!(acard.get_mute()); let muted = try_w!(acard.get_mute());
let _ = try_w!(acard.set_mute(!muted)); let _ = try_w!(acard.set_mute(!muted, AudioUserPopup));
}); });
} }
/* popup_window.connect_event */ /* popup_window.connect_event */
@ -133,42 +134,33 @@ fn grab_devices(window: &gtk::Window) -> Result<()> {
let gdk_window = window.get_window().ok_or("No window?!")?; let gdk_window = window.get_window().ok_or("No window?!")?;
/* Grab the mouse */ /* Grab the mouse */
let m_grab_status = device.grab( let m_grab_status =
&gdk_window, device.grab(&gdk_window,
GrabOwnership::None, GrabOwnership::None,
true, true,
BUTTON_PRESS_MASK, BUTTON_PRESS_MASK,
None, None,
GDK_CURRENT_TIME as u32, GDK_CURRENT_TIME as u32);
);
if m_grab_status != GrabStatus::Success { if m_grab_status != GrabStatus::Success {
warn!( warn!("Could not grab {}",
"Could not grab {}", device.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
} }
/* Grab the keyboard */ /* Grab the keyboard */
let k_dev = device.get_associated_device().ok_or( let k_dev = device.get_associated_device()
"Couldn't get associated device", .ok_or("Couldn't get associated device")?;
)?;
let k_grab_status = k_dev.grab( let k_grab_status = k_dev.grab(&gdk_window,
&gdk_window, GrabOwnership::None,
GrabOwnership::None, true,
true, KEY_PRESS_MASK,
KEY_PRESS_MASK, None,
None, GDK_CURRENT_TIME as u32);
GDK_CURRENT_TIME as u32,
);
if k_grab_status != GrabStatus::Success { if k_grab_status != GrabStatus::Success {
warn!( warn!("Could not grab {}",
"Could not grab {}", k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
} }
return Ok(()); return Ok(());
} }

View File

@ -12,10 +12,9 @@ pub fn init_tray_icon(appstate: &AppS) {
appstate.builder_popup.get_object("popup_window").unwrap(); appstate.builder_popup.get_object("popup_window").unwrap();
tray_icon.connect_activate(move |_| if popup_window.get_visible() { tray_icon.connect_activate(move |_| if popup_window.get_visible() {
popup_window.hide(); popup_window.hide();
} else { } else {
popup_window.show_now(); popup_window.show_now();
}); });
tray_icon.set_visible(true); tray_icon.set_visible(true);
} }