diff --git a/Cargo.toml b/Cargo.toml index 6687e54bc..d9c66c4ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ libc = "^0.2.23" gdk-sys = { git = "https://github.com/gtk-rs/sys" } gtk-sys = { git = "https://github.com/gtk-rs/sys" } glib = { git = "https://github.com/gtk-rs/glib.git" } +glib-sys = { git = "https://github.com/gtk-rs/sys" } ffi = "^0.0.2" flexi_logger = "^0.5.1" log = "^0.3.8" diff --git a/src/app_state.rs b/src/app_state.rs index 1813314f5..af6af5bbf 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -1,10 +1,14 @@ use gtk; +use alsa::poll::PollDescriptors; use alsa::card::Card; use alsa::mixer::{Mixer, SelemId, Selem}; use audio; use errors::*; +// TODO: fix import +use libc::pollfd; + pub struct AppS { /* we keep this to ensure the lifetime is across the whole application */ pub status_icon: gtk::StatusIcon, @@ -12,10 +16,13 @@ pub struct AppS { pub builder_popup: gtk::Builder, } +// TODO: implement free/destructor pub struct AlsaCard { - card: Card, - mixer: Mixer, - selem_id: SelemId, + _cannot_construct: (), + pub card: Card, + pub mixer: Mixer, + pub selem_id: SelemId, + pub watch_ids: Vec, } impl AlsaCard { @@ -35,11 +42,15 @@ impl AlsaCard { elem_name.unwrap_or(String::from("Master")), ).unwrap() .get_id(); + let vec_pollfd = PollDescriptors::get(&mixer)?; + // let watch_ids = return Ok(AlsaCard { + _cannot_construct: (), card: card, mixer: mixer, selem_id: selem_id, + watch_ids: vec![], }); } @@ -53,6 +64,10 @@ impl AlsaCard { return audio::get_vol(&self.selem()); } + pub fn set_vol(&self, new_vol: f64) -> Result<()> { + return audio::set_vol(&self.selem(), new_vol); + } + pub fn has_mute(&self) -> bool { return audio::has_mute(&self.selem()); } @@ -65,3 +80,19 @@ impl AlsaCard { return audio::set_mute(&self.selem(), mute); } } + +pub enum AudioUser { + AudioUserUnknown, + AudioUserPopup, + AudioUserTrayIcon, + AudioUserHotkeys, +} + +enum AudioSignal { + AudioNoCard, + AudioCardInitialized, + AudioCardCleanedUp, + AudioCardDisconnected, + AudioCardError, + AudioValuesChanged, +} diff --git a/src/audio.rs b/src/audio.rs index 81097fe9a..44cdb859c 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -1,13 +1,18 @@ extern crate alsa; -extern crate libc; +extern crate glib_sys; use self::alsa::card::Card; use self::alsa::mixer::{Mixer, Selem, Elem}; use alsa::mixer::SelemChannelId::*; use std::iter::Map; -use self::libc::c_int; +use libc::c_int; +use libc::c_uint; +use libc::c_void; use errors::*; use std::convert::From; +use libc::pollfd; +use app_state; +use std::cell::RefCell; @@ -60,15 +65,41 @@ 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); + /* TODO: precision? Use direction. */ + return _v as i64; +} + pub fn get_vol(selem: &Selem) -> Result { - let (min, max) = selem.get_playback_volume_range(); + let range = 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; + return vol_to_percent(v, range); }); return volume.cherr(); } +pub fn set_vol(selem: &Selem, new_vol: f64) -> Result<()> { + /* auto-unmute */ + if get_mute(selem)? { + set_mute(selem, false)?; + } + + let range = selem.get_playback_volume_range(); + selem.set_playback_volume_all( + percent_to_vol(new_vol, range), + )?; + + return Ok(()); +} + pub fn has_mute(selem: &Selem) -> bool { return selem.has_playback_switch(); } @@ -83,3 +114,37 @@ pub fn set_mute(selem: &Selem, mute: bool) -> Result<()> { let _ = selem.set_playback_switch_all(!mute as i32)?; return Ok(()); } + + + +/* GIO */ + +pub fn watch_poll_descriptors( + polls: Vec, + acard: RefCell, +) -> Vec { + let mut watch_ids: Vec = vec![]; + for poll in polls { + unsafe { + let gioc = glib_sys::g_io_channel_unix_new(poll.fd); + watch_ids.push(glib_sys::g_io_add_watch( + gioc, + glib_sys::GIOCondition::from_bits_truncate( + glib_sys::G_IO_IN.bits() | glib_sys::G_IO_ERR.bits(), + ), + Some(watch_cb), + acard.as_ptr() as glib_sys::gpointer, + )); + } + } + + return vec![]; +} + +extern "C" fn watch_cb( + chan: *mut glib_sys::GIOChannel, + cond: glib_sys::GIOCondition, + data: glib_sys::gpointer, +) -> glib_sys::gboolean { + return true as glib_sys::gboolean; +} diff --git a/src/gui_callbacks.rs b/src/gui_callbacks.rs index c64e96e3d..ff807d5e1 100644 --- a/src/gui_callbacks.rs +++ b/src/gui_callbacks.rs @@ -63,6 +63,23 @@ fn init_popup_window(appstate: &AppS, rc_acard: Rc>) { }); } + /* vol_scale_adj.connect_value_changed */ + { + let vol_scale_adj: Rc = + Rc::new( + appstate.builder_popup.get_object("vol_scale_adj").unwrap(), + ); + + let card = rc_acard.clone(); + let vol_scale = vol_scale_adj.clone(); + vol_scale_adj.connect_value_changed(move |_| { + let acard = card.borrow(); + let val = vol_scale.get_value(); + + try_w!(acard.set_vol(val)); + }); + } + /* mute_check.connect_toggled */ { let mute_check: gtk::CheckButton = diff --git a/src/main.rs b/src/main.rs index 4bd9e4460..d3bf9f32c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ extern crate gtk_sys; extern crate gdk; extern crate gdk_sys; extern crate alsa; +extern crate libc; use gtk::prelude::*; use gdk_sys::GDK_KEY_Escape;