This commit is contained in:
Ospald, Julian 2017-06-29 14:55:07 +02:00
parent a3355f3b72
commit bdee2f4b1c
8 changed files with 133 additions and 103 deletions

View File

@ -1,6 +1,7 @@
use gtk; use gtk;
pub struct AppS { pub struct AppS {
/* we keep this to ensure the lifetime is across the whole application */ /* we keep this to ensure the lifetime is across the whole application */
pub status_icon: gtk::StatusIcon, pub status_icon: gtk::StatusIcon,

View File

@ -1,22 +1,19 @@
use alsa::card::Card; use alsa::card::Card;
use alsa::mixer::{Mixer, Selem, Elem, SelemId}; 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_sys; use glib_sys;
use libc::c_int;
use libc::c_uint; use libc::c_uint;
use libc::c_void;
use libc::pollfd; use libc::pollfd;
use libc::size_t; use libc::size_t;
use myalsa::*; use myalsa::*;
use std::cell::RefCell;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::rc::Rc;
use std::u8; use std::u8;
// TODO: implement free/destructor // TODO: implement free/destructor
pub struct AlsaCard { pub struct AlsaCard {
_cannot_construct: (), _cannot_construct: (),
@ -26,6 +23,8 @@ pub struct AlsaCard {
pub watch_ids: Vec<u32>, pub watch_ids: Vec<u32>,
} }
/* TODO: AlsaCard cleanup */
impl AlsaCard { impl AlsaCard {
pub fn new( pub fn new(
card_name: Option<String>, card_name: Option<String>,
@ -58,36 +57,40 @@ impl AlsaCard {
}); });
} }
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();
} }
pub fn vol(&self) -> Result<f64> { pub fn vol(&self) -> Result<f64> {
return get_vol(&self.selem()); return get_vol(&self.selem());
} }
pub fn set_vol(&self, new_vol: f64) -> Result<()> { pub fn set_vol(&self, new_vol: f64) -> Result<()> {
return set_vol(&self.selem(), new_vol); return set_vol(&self.selem(), new_vol);
} }
pub fn has_mute(&self) -> bool { pub fn has_mute(&self) -> bool {
return has_mute(&self.selem()); return has_mute(&self.selem());
} }
pub fn get_mute(&self) -> Result<bool> { pub fn get_mute(&self) -> Result<bool> {
return get_mute(&self.selem()); return get_mute(&self.selem());
} }
pub fn set_mute(&self, mute: bool) -> Result<()> { pub fn set_mute(&self, mute: bool) -> Result<()> {
return set_mute(&self.selem(), mute); return set_mute(&self.selem(), mute);
} }
} }
pub enum AudioUser { pub enum AudioUser {
AudioUserUnknown, AudioUserUnknown,
AudioUserPopup, AudioUserPopup,
@ -95,6 +98,7 @@ pub enum AudioUser {
AudioUserHotkeys, AudioUserHotkeys,
} }
enum AudioSignal { enum AudioSignal {
AudioNoCard, AudioNoCard,
AudioCardInitialized, AudioCardInitialized,
@ -105,10 +109,6 @@ enum AudioSignal {
} }
fn watch_poll_descriptors( fn watch_poll_descriptors(
polls: Vec<pollfd>, polls: Vec<pollfd>,
mixer: &Mixer, mixer: &Mixer,
@ -135,6 +135,7 @@ fn watch_poll_descriptors(
return watch_ids; return watch_ids;
} }
extern fn watch_cb( extern fn watch_cb(
chan: *mut glib_sys::GIOChannel, chan: *mut glib_sys::GIOChannel,
cond: glib_sys::GIOCondition, cond: glib_sys::GIOCondition,

View File

@ -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: &gtk::Adjustment, scale: f64) {
vol_scale_adj.set_value(scale);
}
pub fn grab_devices(window: &gtk::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(());
}

View File

@ -19,7 +19,6 @@ extern crate gtk_sys;
extern crate libc; extern crate libc;
use app_state::*; use app_state::*;
use gtk::prelude::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -28,13 +27,15 @@ mod errors;
mod app_state; mod app_state;
mod audio; mod audio;
mod gui;
mod gui_callbacks;
mod myalsa; mod myalsa;
mod ui_entry;
mod ui_popup_window;
mod ui_tray_icon;
use audio::AlsaCard; use audio::AlsaCard;
fn main() { fn main() {
gtk::init().unwrap(); gtk::init().unwrap();
@ -55,7 +56,7 @@ fn main() {
.init(Some("info".to_string())) .init(Some("info".to_string()))
.unwrap_or_else(|e| panic!("Logger initialization failed with {}", e)); .unwrap_or_else(|e| panic!("Logger initialization failed with {}", e));
gui_callbacks::init(apps, acard); ui_entry::init(apps, acard);
gtk::main(); gtk::main();
} }

View File

@ -13,14 +13,17 @@ pub fn get_default_alsa_card() -> Card {
return get_alsa_card_by_id(0); return get_alsa_card_by_id(0);
} }
pub fn get_alsa_card_by_id(index: c_int) -> Card { pub fn get_alsa_card_by_id(index: c_int) -> Card {
return Card::new(index); return Card::new(index);
} }
pub fn get_alsa_cards() -> alsa::card::Iter { pub fn get_alsa_cards() -> alsa::card::Iter {
return alsa::card::Iter::new(); return alsa::card::Iter::new();
} }
pub fn get_alsa_card_by_name(name: String) -> Result<Card> { pub fn get_alsa_card_by_name(name: String) -> Result<Card> {
for r_card in get_alsa_cards() { for r_card in get_alsa_cards() {
let card = r_card?; let card = r_card?;
@ -32,10 +35,12 @@ pub fn get_alsa_card_by_name(name: String) -> Result<Card> {
bail!("Not found a matching card named {}", name); bail!("Not found a matching card named {}", name);
} }
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(); return Mixer::new(&format!("hw:{}", card.get_index()), false).cherr();
} }
pub fn get_selem(elem: Elem) -> Selem { pub fn get_selem(elem: Elem) -> Selem {
/* in the ALSA API, there are currently only simple elements, /* in the ALSA API, there are currently only simple elements,
* so this unwrap() should be safe. * so this unwrap() should be safe.
@ -43,10 +48,12 @@ pub fn get_selem(elem: Elem) -> Selem {
return Selem::new(elem).unwrap(); return Selem::new(elem).unwrap();
} }
pub fn get_selems(mixer: &Mixer) -> Map<alsa::mixer::Iter, fn(Elem) -> Selem> { pub fn get_selems(mixer: &Mixer) -> Map<alsa::mixer::Iter, fn(Elem) -> Selem> {
return mixer.iter().map(get_selem); return mixer.iter().map(get_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))?;
@ -58,11 +65,13 @@ pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
bail!("Not found a matching selem named {}", name); bail!("Not found a matching selem named {}", name);
} }
pub fn vol_to_percent(vol: i64, range: (i64, i64)) -> f64 { pub fn vol_to_percent(vol: i64, range: (i64, i64)) -> f64 {
let (min, max) = range; let (min, max) = range;
return ((vol - min) as f64) / ((max - min) as f64) * 100.0; return ((vol - min) as f64) / ((max - min) as f64) * 100.0;
} }
pub fn percent_to_vol(vol: f64, range: (i64, i64)) -> i64 { pub fn percent_to_vol(vol: f64, range: (i64, i64)) -> i64 {
let (min, max) = range; let (min, max) = range;
let _v = vol / 100.0 * ((max - min) as f64) + (min as f64); 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; return _v as i64;
} }
pub fn get_vol(selem: &Selem) -> Result<f64> { pub fn get_vol(selem: &Selem) -> Result<f64> {
let range = selem.get_playback_volume_range(); let range = selem.get_playback_volume_range();
let volume = selem.get_playback_volume(FrontRight).map(|v| { let volume = selem.get_playback_volume(FrontRight).map(|v| {
@ -79,6 +89,7 @@ pub fn get_vol(selem: &Selem) -> Result<f64> {
return volume.cherr(); return volume.cherr();
} }
pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> { pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> {
/* auto-unmute */ /* auto-unmute */
if get_mute(selem)? { if get_mute(selem)? {
@ -93,19 +104,21 @@ pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> {
return Ok(()); return Ok(());
} }
pub fn has_mute(selem: &Selem) -> bool { pub fn has_mute(selem: &Selem) -> bool {
return selem.has_playback_switch(); return selem.has_playback_switch();
} }
pub fn get_mute(selem: &Selem) -> Result<bool> { pub fn get_mute(selem: &Selem) -> Result<bool> {
let val = selem.get_playback_switch(FrontRight)?; let val = selem.get_playback_switch(FrontRight)?;
return Ok(val == 0); return Ok(val == 0);
} }
pub fn set_mute(selem: &Selem, mute: bool) -> Result<()> { pub fn set_mute(selem: &Selem, mute: bool) -> Result<()> {
/* true -> mute, false -> unmute */ /* true -> mute, false -> unmute */
let _ = selem.set_playback_switch_all(!mute as i32)?; let _ = selem.set_playback_switch_all(!mute as i32)?;
return Ok(()); return Ok(());
} }

15
src/ui_entry.rs Normal file
View File

@ -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<RefCell<AlsaCard>>) {
init_tray_icon(&appstate);
init_popup_window(&appstate, rc_acard);
}

View File

@ -1,42 +1,18 @@
use app_state::*; use app_state::*;
use audio::AlsaCard; use audio::AlsaCard;
use errors::*; use errors::*;
use gdk::DeviceExt;
use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK};
use gdk; use gdk;
use gdk_sys::GDK_KEY_Escape; use gdk_sys::{GDK_KEY_Escape, GDK_CURRENT_TIME};
use gtk::prelude::*; use gtk::prelude::*;
use gtk; use gtk;
use gui;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub fn init<'a>(appstate: &'a AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
init_tray_icon(&appstate); pub fn init_popup_window(appstate: &AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
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<RefCell<AlsaCard>>) {
/* popup_window.connect_show */ /* popup_window.connect_show */
{ {
let popup_window: gtk::Window = let popup_window: gtk::Window =
@ -45,17 +21,22 @@ fn init_popup_window(appstate: &AppS, rc_acard: Rc<RefCell<AlsaCard>>) {
appstate.builder_popup.get_object("vol_scale_adj").unwrap(); appstate.builder_popup.get_object("vol_scale_adj").unwrap();
let mute_check: gtk::CheckButton = let mute_check: gtk::CheckButton =
appstate.builder_popup.get_object("mute_check").unwrap(); 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(); let card = rc_acard.clone();
popup_window.connect_show(move |_| { popup_window.connect_show(move |w| {
let acard = card.borrow(); let acard = card.borrow();
let cur_vol = try_w!(acard.vol()); let cur_vol = try_w!(acard.vol());
println!("Cur vol: {}", cur_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(); let muted = acard.get_mute();
update_mute_check(&mute_check, muted); 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<RefCell<AlsaCard>>) {
} }
} }
fn update_mute_check(check_button: &gtk::CheckButton, muted: Result<bool>) { fn update_mute_check(check_button: &gtk::CheckButton, muted: Result<bool>) {
match muted { match muted {
Ok(val) => { Ok(val) => {
@ -138,3 +120,55 @@ fn update_mute_check(check_button: &gtk::CheckButton, muted: Result<bool>) {
} }
} }
} }
fn set_slider(vol_scale_adj: &gtk::Adjustment, scale: f64) {
vol_scale_adj.set_value(scale);
}
fn grab_devices(window: &gtk::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(());
}

21
src/ui_tray_icon.rs Normal file
View File

@ -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);
}