This commit is contained in:
Julian Ospald 2017-06-28 00:23:12 +02:00
parent 3f19417f75
commit 0daff4d6b8
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
6 changed files with 242 additions and 111 deletions

19
src/app_state.rs Normal file
View File

@ -0,0 +1,19 @@
use gtk;
use alsa::card::Card;
use alsa::mixer::{Mixer, Selem};
use std::cell::Cell;
pub struct AppS {
/* we keep this to ensure the lifetime is across the whole application */
pub status_icon: gtk::StatusIcon,
pub builder_popup: gtk::Builder,
}
pub struct AlsaCard<'a> {
pub card: Cell<Card>,
pub mixer: Cell<Mixer>,
pub selem: Cell<Selem<'a>>,
}

View File

@ -1,5 +1,4 @@
extern crate alsa;
extern crate std;
extern crate libc;
use self::alsa::card::Card;
@ -24,7 +23,7 @@ pub fn get_alsa_cards() -> alsa::card::Iter {
return alsa::card::Iter::new();
}
pub fn get_mixer(card: Card) -> Result<Mixer> {
pub fn get_mixer(card: &Card) -> Result<Mixer> {
return Mixer::new(&format!("hw:{}", card.get_index()), false).cherr();
}
@ -39,9 +38,11 @@ pub fn get_selems(mixer: &Mixer) -> Map<alsa::mixer::Iter, fn(Elem) -> Selem> {
return mixer.iter().map(get_selem);
}
pub fn get_selem_by_name<'a>(mixer: &'a Mixer, name: String) -> Result<Selem> {
pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
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 {
return Ok(selem);
@ -50,7 +51,7 @@ pub fn get_selem_by_name<'a>(mixer: &'a Mixer, name: String) -> Result<Selem> {
bail!("Not found a matching selem named {}", name);
}
pub fn get_vol(selem: Selem) -> Result<f64> {
pub fn get_vol(selem: &Selem) -> Result<f64> {
let (min, max) = selem.get_playback_volume_range();
let volume = selem.get_playback_volume(FrontRight).map(|v| {
return ((v - min) as f64) / ((max - min) as f64) * 100.0;
@ -58,3 +59,19 @@ pub fn get_vol(selem: Selem) -> Result<f64> {
return volume.cherr();
}
pub fn has_mute(selem: &Selem) -> bool {
return selem.has_playback_switch();
}
pub fn get_mute(selem: &Selem) -> Result<bool> {
let val = selem.get_playback_switch(FrontRight)?;
return Ok(val == 0);
}
pub fn set_mute(selem: &Selem, mute: bool) -> Result<()> {
/* true -> mute, false -> unmute */
let _ = selem.set_playback_switch_all(!mute as i32)?;
return Ok(());
}

View File

@ -15,7 +15,8 @@ pub trait CHErr {
}
impl<A, E: std::error::Error> CHErr for std::result::Result<A, E>
where Error: std::convert::From<E> {
where Error: std::convert::From<E>
{
type Item = A;
fn cherr(self) -> Result<Self::Item> {
return self.map_err(From::from);
@ -30,6 +31,9 @@ macro_rules! try_w {
};
($expr:expr, $fmt:expr, $($arg:tt)+) => {
try_wr!($expr, (), $fmt, $(arg)+)
};
($expr:expr, $fmt:expr) => {
try_wr!($expr, (), $fmt)
}
}
@ -42,6 +46,14 @@ macro_rules! try_wr {
return $ret;
},
});
($expr:expr, $ret:expr, $fmt:expr) => (match $expr {
std::result::Result::Ok(val) => val,
std::result::Result::Err(err) => {
warn!("Original error: {:?}", err);
warn!($fmt);
return $ret;
},
});
($expr:expr, $ret:expr, $fmt:expr, $($arg:tt)+) => (match $expr {
std::result::Result::Ok(val) => val,
std::result::Result::Err(err) => {
@ -51,3 +63,13 @@ macro_rules! try_wr {
},
})
}
#[macro_export]
macro_rules! try_r {
($expr:expr, $ret:expr) => (match $expr {
std::result::Result::Ok(val) => val,
std::result::Result::Err(err) => {
return $ret;
},
});
}

View File

@ -23,42 +23,33 @@ pub fn grab_devices(window: &gtk::Window) -> Result<()> {
let gdk_window = window.get_window().ok_or("No window?!")?;
/* Grab the mouse */
let m_grab_status = device.grab(
&gdk_window,
GrabOwnership::None,
true,
BUTTON_PRESS_MASK,
None,
GDK_CURRENT_TIME as u32,
);
let m_grab_status =
device.grab(&gdk_window,
GrabOwnership::None,
true,
BUTTON_PRESS_MASK,
None,
GDK_CURRENT_TIME as u32);
if m_grab_status != GrabStatus::Success {
warn!(
"Could not grab {}",
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
warn!("Could not grab {}",
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
}
/* Grab the keyboard */
let k_dev = device.get_associated_device().ok_or(
"Couldn't get associated device",
)?;
let k_dev = device.get_associated_device()
.ok_or("Couldn't get associated device")?;
let k_grab_status = k_dev.grab(
&gdk_window,
GrabOwnership::None,
true,
KEY_PRESS_MASK,
None,
GDK_CURRENT_TIME as u32,
);
let k_grab_status = k_dev.grab(&gdk_window,
GrabOwnership::None,
true,
KEY_PRESS_MASK,
None,
GDK_CURRENT_TIME as u32);
if k_grab_status != GrabStatus::Success {
warn!(
"Could not grab {}",
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
warn!("Could not grab {}",
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
}
return Ok(());
}

134
src/gui_callbacks.rs Normal file
View File

@ -0,0 +1,134 @@
extern crate gtk;
extern crate gtk_sys;
extern crate gdk;
extern crate gdk_sys;
extern crate alsa;
extern crate std;
use gtk::prelude::*;
use gdk_sys::GDK_KEY_Escape;
use gui;
use audio;
use app_state::*;
use errors::*;
pub fn init<'a>(appstate: &'a AppS) {
init_tray_icon(&appstate);
init_popup_window(&appstate);
}
fn init_tray_icon(appstate: &AppS) {
let ref tray_icon = appstate.status_icon;
let popup_window: gtk::Window =
appstate.builder_popup.get_object("popup_window").unwrap();
let vol_scale: gtk::Scale =
appstate.builder_popup.get_object("vol_scale").unwrap();
tray_icon.connect_activate(move |_| if popup_window.get_visible() {
popup_window.hide();
} else {
popup_window.show_now();
vol_scale.grab_focus();
try_w!(gui::grab_devices(&popup_window));
});
tray_icon.set_visible(true);
}
fn init_popup_window(appstate: &AppS) {
/* popup_window.connect_show */
{
let popup_window: gtk::Window =
appstate.builder_popup.get_object("popup_window").unwrap();
let vol_scale_adj: gtk::Adjustment =
appstate.builder_popup.get_object("vol_scale_adj").unwrap();
let mute_check: gtk::CheckButton =
appstate.builder_popup.get_object("mute_check").unwrap();
popup_window.connect_show(move |_| {
let alsa_card = audio::get_default_alsa_card();
let mixer = try_w!(audio::get_mixer(&alsa_card));
let selem = try_w!(audio::get_selem_by_name(
&mixer,
String::from("Master"),
));
let cur_vol = try_w!(audio::get_vol(&selem));
gui::set_slider(&vol_scale_adj, cur_vol);
let muted = audio::get_mute(&selem);
update_mute_check(&mute_check, muted);
});
}
/* mute_check.connect_toggled */
{
let mute_check: gtk::CheckButton =
appstate.builder_popup.get_object("mute_check").unwrap();
mute_check.connect_toggled(move |_| {
let alsa_card = audio::get_default_alsa_card();
let mixer = try_w!(audio::get_mixer(&alsa_card));
let selem = try_w!(audio::get_selem_by_name(
&mixer,
String::from("Master"),
));
let muted = try_w!(audio::get_mute(&selem));
let _ = try_w!(audio::set_mute(&selem, !muted));
});
}
/* popup_window.connect_event */
{
let popup_window: gtk::Window =
appstate.builder_popup.get_object("popup_window").unwrap();
popup_window.connect_event(move |w, e| {
match gdk::Event::get_event_type(e) {
gdk::EventType::GrabBroken => w.hide(),
gdk::EventType::KeyPress => {
let key: gdk::EventKey = e.clone().downcast().unwrap();
if key.get_keyval() == (GDK_KEY_Escape as u32) {
w.hide();
}
}
gdk::EventType::ButtonPress => {
let device = try_wr!(
gtk::get_current_event_device().ok_or(
"No current event device!",
),
Inhibit(false)
);
let (window, _, _) =
gdk::DeviceExt::get_window_at_position(&device);
if window.is_none() {
w.hide();
}
}
_ => (),
}
return Inhibit(false);
});
}
}
fn update_mute_check(check_button: &gtk::CheckButton, muted: Result<bool>) {
match muted {
Ok(val) => {
check_button.set_active(val);
check_button.set_tooltip_text("");
},
Err(_) => {
/* can't figure out whether channel is muted, grey out */
check_button.set_active(true);
check_button.set_sensitive(false);
check_button.set_tooltip_text("Soundcard has no mute switch");
}
}
}

View File

@ -11,103 +11,51 @@ extern crate gdk;
extern crate gdk_sys;
extern crate alsa;
// use std::ops::Deref;
// use std::boxed::Box;
// use std::rc::Rc;
// use std::sync::Arc;
use gtk::prelude::*;
use gdk_sys::GDK_KEY_Escape;
use app_state::*;
use std::cell::Cell;
use std::boxed::Box;
#[macro_use]
mod errors;
mod audio;
mod gui;
mod debug;
mod gui_callbacks;
mod app_state;
fn main() {
gtk::init().unwrap();
let ref apps = AppS {
status_icon: gtk::StatusIcon::new_from_icon_name("pnmixer"),
builder_popup: gtk::Builder::new_from_string(include_str!("../data/ui/popup-window-vertical.glade")),
};
let alsa_card = audio::get_default_alsa_card();
let mixer = audio::get_mixer(&alsa_card).unwrap();
let selem = audio::get_selem_by_name(
&mixer,
String::from("Master"),
).unwrap();
let ref acard = AlsaCard {
card: Cell::new(alsa_card),
mixer: Cell::new(mixer),
selem: Cell::new(selem),
};
flexi_logger::LogOptions::new()
.log_to_file(false)
// ... your configuration options go here ...
.init(Some("info".to_string()))
.unwrap_or_else(|e| panic!("Logger initialization failed with {}", e));
let tray_icon = gtk::StatusIcon::new_from_icon_name("pnmixer");
let glade_src = include_str!("../data/ui/popup-window-vertical.glade");
let builder_popup = gtk::Builder::new_from_string(glade_src);
{
let popup_window: gtk::Window =
builder_popup.get_object("popup_window").unwrap();
let vol_scale: gtk::Scale =
builder_popup.get_object("vol_scale").unwrap();
tray_icon.connect_activate(move |_| if popup_window.get_visible() {
popup_window.hide();
} else {
popup_window.show_now();
vol_scale.grab_focus();
gui::grab_devices(&popup_window);
});
}
{
let popup_window: gtk::Window =
builder_popup.get_object("popup_window").unwrap();
let vol_scale_adj: gtk::Adjustment =
builder_popup.get_object("vol_scale_adj").unwrap();
popup_window.connect_show(move |_| {
let alsa_card = audio::get_default_alsa_card();
let mixer = try_w!(audio::get_mixer(alsa_card));
let selem = try_w!(audio::get_selem_by_name(
&mixer,
String::from("Master"),
));
let cur_vol = try_w!(audio::get_vol(selem));
gui::set_slider(&vol_scale_adj, cur_vol);
});
}
{
let popup_window: gtk::Window =
builder_popup.get_object("popup_window").unwrap();
popup_window.connect_event(move |w, e| {
match gdk::Event::get_event_type(e) {
gdk::EventType::GrabBroken => w.hide(),
gdk::EventType::KeyPress => {
let key: gdk::EventKey = e.clone().downcast().unwrap();
if key.get_keyval() == (GDK_KEY_Escape as u32) {
w.hide();
}
}
gdk::EventType::ButtonPress => {
let device = try_wr!(
gtk::get_current_event_device().ok_or(
"No current event device!",
),
Inhibit(false)
);
let (window, _, _) =
gdk::DeviceExt::get_window_at_position(&device);
if window.is_none() {
w.hide();
}
}
_ => (),
}
return Inhibit(false);
});
}
tray_icon.set_visible(true);
gui_callbacks::init(apps);
gtk::main();
}