From 0daff4d6b8f96f4277b7db1448591524cbce043d Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Wed, 28 Jun 2017 00:23:12 +0200 Subject: [PATCH] Update --- src/app_state.rs | 19 ++++++ src/audio.rs | 27 +++++++-- src/errors.rs | 24 +++++++- src/gui.rs | 47 ++++++--------- src/gui_callbacks.rs | 134 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 102 ++++++++------------------------ 6 files changed, 242 insertions(+), 111 deletions(-) create mode 100644 src/app_state.rs create mode 100644 src/gui_callbacks.rs diff --git a/src/app_state.rs b/src/app_state.rs new file mode 100644 index 000000000..3b76822c6 --- /dev/null +++ b/src/app_state.rs @@ -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, + pub mixer: Cell, + pub selem: Cell>, +} + diff --git a/src/audio.rs b/src/audio.rs index eceaffa6d..46914c15c 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -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 { +pub fn get_mixer(card: &Card) -> Result { return Mixer::new(&format!("hw:{}", card.get_index()), false).cherr(); } @@ -39,9 +38,11 @@ pub fn get_selems(mixer: &Mixer) -> Map Selem> { return mixer.iter().map(get_selem); } -pub fn get_selem_by_name<'a>(mixer: &'a Mixer, name: String) -> Result { +pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result { 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 { bail!("Not found a matching selem named {}", name); } -pub fn get_vol(selem: Selem) -> Result { +pub fn get_vol(selem: &Selem) -> Result { 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 { return volume.cherr(); } + +pub fn has_mute(selem: &Selem) -> bool { + return selem.has_playback_switch(); +} + +pub fn get_mute(selem: &Selem) -> Result { + 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(()); +} + diff --git a/src/errors.rs b/src/errors.rs index ba58831fc..126a0b673 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -15,7 +15,8 @@ pub trait CHErr { } impl CHErr for std::result::Result - where Error: std::convert::From { + where Error: std::convert::From +{ type Item = A; fn cherr(self) -> Result { 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; + }, + }); +} diff --git a/src/gui.rs b/src/gui.rs index 4687837c3..9d2a82fbf 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -23,42 +23,33 @@ pub fn grab_devices(window: >k::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(()); } - diff --git a/src/gui_callbacks.rs b/src/gui_callbacks.rs new file mode 100644 index 000000000..847cfcbea --- /dev/null +++ b/src/gui_callbacks.rs @@ -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: >k::CheckButton, muted: Result) { + 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"); + } + } +} + diff --git a/src/main.rs b/src/main.rs index 64279258c..a1ebc2965 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(); }