diff --git a/src/app_state.rs b/src/app_state.rs index 63c6fd9aa..268461a23 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,6 +1,7 @@ use gtk; + pub struct AppS { /* we keep this to ensure the lifetime is across the whole application */ pub status_icon: gtk::StatusIcon, diff --git a/src/audio.rs b/src/audio.rs index 3d8d3740c..fc773365d 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,22 +1,19 @@ use alsa::card::Card; -use alsa::mixer::{Mixer, Selem, Elem, SelemId}; +use alsa::mixer::{Mixer, Selem, SelemId}; use alsa::poll::PollDescriptors; use alsa_sys; use errors::*; use glib_sys; -use libc::c_int; use libc::c_uint; -use libc::c_void; use libc::pollfd; use libc::size_t; use myalsa::*; -use std::cell::RefCell; use std::mem; use std::ptr; -use std::rc::Rc; use std::u8; + // TODO: implement free/destructor pub struct AlsaCard { _cannot_construct: (), @@ -26,6 +23,8 @@ pub struct AlsaCard { pub watch_ids: Vec, } + +/* TODO: AlsaCard cleanup */ impl AlsaCard { pub fn new( card_name: Option, @@ -58,36 +57,40 @@ impl AlsaCard { }); } + pub fn selem(&self) -> Selem { return get_selems(&self.mixer) .nth(self.selem_id.get_index() as usize) .unwrap(); } + pub fn vol(&self) -> Result { return get_vol(&self.selem()); } + pub fn set_vol(&self, new_vol: f64) -> Result<()> { return set_vol(&self.selem(), new_vol); } + pub fn has_mute(&self) -> bool { return has_mute(&self.selem()); } + pub fn get_mute(&self) -> Result { return get_mute(&self.selem()); } + pub fn set_mute(&self, mute: bool) -> Result<()> { return set_mute(&self.selem(), mute); } - - - } + pub enum AudioUser { AudioUserUnknown, AudioUserPopup, @@ -95,6 +98,7 @@ pub enum AudioUser { AudioUserHotkeys, } + enum AudioSignal { AudioNoCard, AudioCardInitialized, @@ -105,10 +109,6 @@ enum AudioSignal { } - - - - fn watch_poll_descriptors( polls: Vec, mixer: &Mixer, @@ -135,6 +135,7 @@ fn watch_poll_descriptors( return watch_ids; } + extern fn watch_cb( chan: *mut glib_sys::GIOChannel, cond: glib_sys::GIOCondition, diff --git a/src/gui.rs b/src/gui.rs deleted file mode 100644 index 548d4a390..000000000 --- a/src/gui.rs +++ /dev/null @@ -1,56 +0,0 @@ -use errors::*; -use gdk::DeviceExt; -use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK}; -use gdk_sys::GDK_CURRENT_TIME; -use gtk::prelude::*; -use gtk; - - -pub fn set_slider(vol_scale_adj: >k::Adjustment, scale: f64) { - vol_scale_adj.set_value(scale); -} - -pub fn grab_devices(window: >k::Window) -> Result<()> { - let device = gtk::get_current_event_device().ok_or("No current device")?; - - 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, - ); - - if m_grab_status != GrabStatus::Success { - 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_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")) - ); - } - - return Ok(()); -} diff --git a/src/main.rs b/src/main.rs index a236bd160..966462cd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,6 @@ extern crate gtk_sys; extern crate libc; use app_state::*; -use gtk::prelude::*; use std::cell::RefCell; use std::rc::Rc; @@ -28,13 +27,15 @@ mod errors; mod app_state; mod audio; -mod gui; -mod gui_callbacks; mod myalsa; +mod ui_entry; +mod ui_popup_window; +mod ui_tray_icon; use audio::AlsaCard; + fn main() { gtk::init().unwrap(); @@ -55,7 +56,7 @@ fn main() { .init(Some("info".to_string())) .unwrap_or_else(|e| panic!("Logger initialization failed with {}", e)); - gui_callbacks::init(apps, acard); + ui_entry::init(apps, acard); gtk::main(); } diff --git a/src/myalsa.rs b/src/myalsa.rs index 3cb324f24..1e59524f2 100644 --- a/src/myalsa.rs +++ b/src/myalsa.rs @@ -13,14 +13,17 @@ pub fn get_default_alsa_card() -> Card { return get_alsa_card_by_id(0); } + pub fn get_alsa_card_by_id(index: c_int) -> Card { return Card::new(index); } + pub fn get_alsa_cards() -> alsa::card::Iter { return alsa::card::Iter::new(); } + pub fn get_alsa_card_by_name(name: String) -> Result { for r_card in get_alsa_cards() { let card = r_card?; @@ -32,10 +35,12 @@ pub fn get_alsa_card_by_name(name: String) -> Result { bail!("Not found a matching card named {}", name); } + pub fn get_mixer(card: &Card) -> Result { return Mixer::new(&format!("hw:{}", card.get_index()), false).cherr(); } + pub fn get_selem(elem: Elem) -> Selem { /* in the ALSA API, there are currently only simple elements, * so this unwrap() should be safe. @@ -43,10 +48,12 @@ pub fn get_selem(elem: Elem) -> Selem { return Selem::new(elem).unwrap(); } + pub fn get_selems(mixer: &Mixer) -> Map Selem> { return mixer.iter().map(get_selem); } + 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))?; @@ -58,11 +65,13 @@ pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result { bail!("Not found a matching selem named {}", name); } + pub fn vol_to_percent(vol: i64, range: (i64, i64)) -> f64 { let (min, max) = range; return ((vol - min) as f64) / ((max - min) as f64) * 100.0; } + pub fn percent_to_vol(vol: f64, range: (i64, i64)) -> i64 { let (min, max) = range; let _v = vol / 100.0 * ((max - min) as f64) + (min as f64); @@ -70,6 +79,7 @@ pub fn percent_to_vol(vol: f64, range: (i64, i64)) -> i64 { return _v as i64; } + pub fn get_vol(selem: &Selem) -> Result { let range = selem.get_playback_volume_range(); let volume = selem.get_playback_volume(FrontRight).map(|v| { @@ -79,6 +89,7 @@ pub fn get_vol(selem: &Selem) -> Result { return volume.cherr(); } + pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> { /* auto-unmute */ if get_mute(selem)? { @@ -93,19 +104,21 @@ pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> { return Ok(()); } + 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/ui_entry.rs b/src/ui_entry.rs new file mode 100644 index 000000000..567e80d2d --- /dev/null +++ b/src/ui_entry.rs @@ -0,0 +1,15 @@ +use app_state::*; +use audio::AlsaCard; +use std::cell::RefCell; +use std::rc::Rc; +use ui_popup_window::*; +use ui_tray_icon::*; + + + +pub fn init<'a>(appstate: &'a AppS, rc_acard: Rc>) { + + init_tray_icon(&appstate); + init_popup_window(&appstate, rc_acard); +} + diff --git a/src/gui_callbacks.rs b/src/ui_popup_window.rs similarity index 66% rename from src/gui_callbacks.rs rename to src/ui_popup_window.rs index 1f09d49e1..9aa2ac1ea 100644 --- a/src/gui_callbacks.rs +++ b/src/ui_popup_window.rs @@ -1,42 +1,18 @@ use app_state::*; use audio::AlsaCard; use errors::*; +use gdk::DeviceExt; +use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK}; use gdk; -use gdk_sys::GDK_KEY_Escape; +use gdk_sys::{GDK_KEY_Escape, GDK_CURRENT_TIME}; use gtk::prelude::*; use gtk; -use gui; use std::cell::RefCell; use std::rc::Rc; -pub fn init<'a>(appstate: &'a AppS, rc_acard: Rc>) { - init_tray_icon(&appstate); - init_popup_window(&appstate, rc_acard); -} - - -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, rc_acard: Rc>) { +pub fn init_popup_window(appstate: &AppS, rc_acard: Rc>) { /* popup_window.connect_show */ { let popup_window: gtk::Window = @@ -45,17 +21,22 @@ fn init_popup_window(appstate: &AppS, rc_acard: Rc>) { appstate.builder_popup.get_object("vol_scale_adj").unwrap(); let mute_check: gtk::CheckButton = appstate.builder_popup.get_object("mute_check").unwrap(); + let vol_scale: gtk::Scale = + appstate.builder_popup.get_object("vol_scale").unwrap(); let card = rc_acard.clone(); - popup_window.connect_show(move |_| { + popup_window.connect_show(move |w| { let acard = card.borrow(); let cur_vol = try_w!(acard.vol()); println!("Cur vol: {}", cur_vol); - gui::set_slider(&vol_scale_adj, cur_vol); + set_slider(&vol_scale_adj, cur_vol); let muted = acard.get_mute(); update_mute_check(&mute_check, muted); + + vol_scale.grab_focus(); + try_w!(grab_devices(w)); }); } @@ -124,6 +105,7 @@ fn init_popup_window(appstate: &AppS, rc_acard: Rc>) { } } + fn update_mute_check(check_button: >k::CheckButton, muted: Result) { match muted { Ok(val) => { @@ -138,3 +120,55 @@ fn update_mute_check(check_button: >k::CheckButton, muted: Result) { } } } + + +fn set_slider(vol_scale_adj: >k::Adjustment, scale: f64) { + vol_scale_adj.set_value(scale); +} + + +fn grab_devices(window: >k::Window) -> Result<()> { + let device = gtk::get_current_event_device().ok_or("No current device")?; + + 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, + ); + + if m_grab_status != GrabStatus::Success { + 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_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")) + ); + } + + return Ok(()); +} + diff --git a/src/ui_tray_icon.rs b/src/ui_tray_icon.rs new file mode 100644 index 000000000..8bbaac863 --- /dev/null +++ b/src/ui_tray_icon.rs @@ -0,0 +1,21 @@ +use app_state::*; +use gtk::prelude::*; +use gtk; + + + +pub 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(); + + tray_icon.connect_activate(move |_| if popup_window.get_visible() { + popup_window.hide(); + } else { + popup_window.show_now(); + }); + tray_icon.set_visible(true); +} +