This commit is contained in:
Julian Ospald 2017-07-07 22:10:45 +02:00
parent 3096347ae4
commit a5bfb1e27d
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
12 changed files with 559 additions and 563 deletions

View File

@ -36,14 +36,19 @@ pub struct AlsaCard {
impl AlsaCard { impl AlsaCard {
pub fn new( pub fn new(card_name: Option<String>,
card_name: Option<String>, elem_name: Option<String>,
elem_name: Option<String>, cb: Rc<Fn(AlsaEvent)>)
cb: Rc<Fn(AlsaEvent)>, -> Result<Box<AlsaCard>> {
) -> Result<Box<AlsaCard>> {
let card = { let card = {
match card_name { match card_name {
Some(name) => get_alsa_card_by_name(name)?, Some(name) => {
if name == "(default)" {
get_default_alsa_card()
} else {
get_alsa_card_by_name(name)?
}
},
None => get_default_alsa_card(), None => get_default_alsa_card(),
} }
}; };
@ -51,26 +56,26 @@ impl AlsaCard {
let vec_pollfd = PollDescriptors::get(&mixer)?; let vec_pollfd = PollDescriptors::get(&mixer)?;
let selem_id = get_selem_by_name( let selem_id =
&mixer, get_selem_by_name(&mixer,
elem_name.unwrap_or(String::from("Master")), elem_name.unwrap_or(String::from("Master")))
).unwrap() .unwrap()
.get_id(); .get_id();
let acard = Box::new(AlsaCard { let acard = Box::new(AlsaCard {
_cannot_construct: (), _cannot_construct: (),
card: card, card: card,
mixer: mixer, mixer: mixer,
selem_id: selem_id, selem_id: selem_id,
watch_ids: Cell::new(vec![]), watch_ids: Cell::new(vec![]),
cb: cb, cb: cb,
}); });
/* TODO: callback is registered here, which must be unregistered /* TODO: callback is registered here, which must be unregistered
* when the card is destroyed!! * when the card is destroyed!!
* poll descriptors must be unwatched too */ * poll descriptors must be unwatched too */
let watch_ids = let watch_ids = AlsaCard::watch_poll_descriptors(vec_pollfd,
AlsaCard::watch_poll_descriptors(vec_pollfd, acard.as_ref()); acard.as_ref());
acard.watch_ids.set(watch_ids); acard.watch_ids.set(watch_ids);
return Ok(acard); return Ok(acard);
@ -83,7 +88,9 @@ impl AlsaCard {
pub fn chan_name(&self) -> Result<String> { pub fn chan_name(&self) -> Result<String> {
let n = self.selem_id.get_name().map(|y| String::from(y))?; let n = self.selem_id
.get_name()
.map(|y| String::from(y))?;
return Ok(n); return Ok(n);
} }
@ -112,9 +119,7 @@ impl AlsaCard {
} }
let range = selem.get_playback_volume_range(); let range = selem.get_playback_volume_range();
selem.set_playback_volume_all( selem.set_playback_volume_all(percent_to_vol(new_vol, range))?;
percent_to_vol(new_vol, range),
)?;
return Ok(()); return Ok(());
} }
@ -141,10 +146,9 @@ impl AlsaCard {
} }
fn watch_poll_descriptors( fn watch_poll_descriptors(polls: Vec<pollfd>,
polls: Vec<pollfd>, acard: &AlsaCard)
acard: &AlsaCard, -> Vec<c_uint> {
) -> Vec<c_uint> {
let mut watch_ids: Vec<c_uint> = vec![]; let mut watch_ids: Vec<c_uint> = vec![];
let acard_ptr = let acard_ptr =
unsafe { mem::transmute::<&AlsaCard, glib_sys::gpointer>(acard) }; unsafe { mem::transmute::<&AlsaCard, glib_sys::gpointer>(acard) };
@ -201,11 +205,10 @@ impl Drop for AlsaCard {
} }
extern "C" fn watch_cb( extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
chan: *mut glib_sys::GIOChannel, cond: glib_sys::GIOCondition,
cond: glib_sys::GIOCondition, data: glib_sys::gpointer)
data: glib_sys::gpointer, -> glib_sys::gboolean {
) -> glib_sys::gboolean {
let acard = let acard =
unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) }; unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) };
@ -224,15 +227,14 @@ extern "C" fn watch_cb(
let mut buf: Vec<u8> = vec![0; 256]; let mut buf: Vec<u8> = vec![0; 256];
while sread > 0 { while sread > 0 {
let stat: glib_sys::GIOStatus = unsafe { let stat: glib_sys::GIOStatus =
glib_sys::g_io_channel_read_chars( unsafe {
chan, glib_sys::g_io_channel_read_chars(chan,
buf.as_mut_ptr() as *mut u8, buf.as_mut_ptr() as *mut u8,
256, 256,
&mut sread as *mut size_t, &mut sread as *mut size_t,
ptr::null_mut(), ptr::null_mut())
) };
};
match stat { match stat {
glib_sys::G_IO_STATUS_AGAIN => { glib_sys::G_IO_STATUS_AGAIN => {

View File

@ -23,22 +23,20 @@ pub struct AppS {
impl AppS { impl AppS {
pub fn new() -> AppS { pub fn new() -> AppS {
let builder_popup_window = gtk::Builder::new_from_string(include_str!( let builder_popup_window =
"../data/ui/popup-window.glade" gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/popup-window.glade")));
)); let builder_popup_menu = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/popup-menu.glade")));
let builder_popup_menu = gtk::Builder::new_from_string(
include_str!("../data/ui/popup-menu.glade"),
);
let prefs = RefCell::new(Prefs::new().unwrap()); let prefs = RefCell::new(Prefs::new().unwrap());
let gui = let gui =
Gui::new(builder_popup_window, builder_popup_menu, &prefs.borrow()); Gui::new(builder_popup_window, builder_popup_menu, &prefs.borrow());
return AppS { return AppS {
_cant_construct: (), _cant_construct: (),
gui: gui, gui: gui,
audio: Audio::new(None, Some(String::from("Master"))).unwrap(), audio: Audio::new(None, Some(String::from("Master")))
prefs: prefs, .unwrap(),
}; prefs: prefs,
};
} }
@ -46,11 +44,9 @@ impl AppS {
pub fn update_tray_icon(&self) -> Result<()> { pub fn update_tray_icon(&self) -> Result<()> {
debug!("Update tray icon!"); debug!("Update tray icon!");
return self.gui.tray_icon.update_all( return self.gui.tray_icon.update_all(&self.prefs.borrow(),
&self.prefs.borrow(), &self.audio,
&self.audio, None);
None,
);
} }
pub fn update_popup_window(&self) -> Result<()> { pub fn update_popup_window(&self) -> Result<()> {
@ -61,5 +57,4 @@ impl AppS {
pub fn show_preferences(&self) { pub fn show_preferences(&self) {
// show_prefs_dialog(self); // show_prefs_dialog(self);
} }
} }

View File

@ -73,10 +73,9 @@ pub struct Audio {
impl Audio { impl Audio {
pub fn new( pub fn new(card_name: Option<String>,
card_name: Option<String>, elem_name: Option<String>)
elem_name: Option<String>, -> Result<Audio> {
) -> Result<Audio> {
let handlers = Handlers::new(); let handlers = Handlers::new();
let last_action_timestamp = Rc::new(RefCell::new(0)); let last_action_timestamp = Rc::new(RefCell::new(0));
@ -85,8 +84,10 @@ impl Audio {
let myhandler = handlers.clone(); let myhandler = handlers.clone();
let ts = last_action_timestamp.clone(); let ts = last_action_timestamp.clone();
Rc::new(move |event| { Rc::new(move |event| {
on_alsa_event(&mut *ts.borrow_mut(), &myhandler.borrow(), event) on_alsa_event(&mut *ts.borrow_mut(),
}) &myhandler.borrow(),
event)
})
}; };
let audio = Audio { let audio = Audio {
@ -101,40 +102,44 @@ impl Audio {
} }
pub fn switch_acard( pub fn switch_acard(&self,
&self, card_name: Option<String>,
card_name: Option<String>, elem_name: Option<String>,
elem_name: Option<String>, user: AudioUser)
user: AudioUser, -> Result<()> {
) -> Result<()> {
debug!("Switching cards"); debug!("Switching cards");
debug!( debug!("Old card name: {}",
"Old card name: {}", self.acard
self.acard.borrow().card_name().unwrap() .borrow()
); .card_name()
debug!( .unwrap());
"Old chan name: {}", debug!("Old chan name: {}",
self.acard.borrow().chan_name().unwrap() self.acard
); .borrow()
let cb = self.acard.borrow().cb.clone(); .chan_name()
.unwrap());
let cb = self.acard
.borrow()
.cb
.clone();
{ {
let mut ac = self.acard.borrow_mut(); let mut ac = self.acard.borrow_mut();
*ac = AlsaCard::new(card_name, elem_name, cb)?; *ac = AlsaCard::new(card_name, elem_name, cb)?;
} }
debug!( debug!("Old card name: {}",
"Old card name: {}", self.acard
self.acard.borrow().card_name().unwrap() .borrow()
); .card_name()
debug!( .unwrap());
"Old chan name: {}", debug!("Old chan name: {}",
self.acard.borrow().chan_name().unwrap() self.acard
); .borrow()
.chan_name()
.unwrap());
invoke_handlers( invoke_handlers(&self.handlers.borrow(),
&self.handlers.borrow(), AudioSignal::CardInitialized,
AudioSignal::CardInitialized, user);
user,
);
return Ok(()); return Ok(());
} }
@ -167,20 +172,24 @@ impl Audio {
*rc = glib::get_monotonic_time(); *rc = glib::get_monotonic_time();
} }
debug!( debug!("Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
"Setting vol on card {:?} and chan {:?} to {:?} by user {:?}", self.acard
self.acard.borrow().card_name().unwrap(), .borrow()
self.acard.borrow().chan_name().unwrap(), .card_name()
new_vol, .unwrap(),
user self.acard
); .borrow()
self.acard.borrow().set_vol(new_vol)?; .chan_name()
.unwrap(),
new_vol,
user);
self.acard
.borrow()
.set_vol(new_vol)?;
invoke_handlers( invoke_handlers(&self.handlers.borrow(),
&self.handlers.borrow(), AudioSignal::ValuesChanged,
AudioSignal::ValuesChanged, user);
user,
);
return Ok(()); return Ok(());
} }
@ -193,21 +202,23 @@ impl Audio {
let old_vol = self.vol()?; let old_vol = self.vol()?;
let new_vol = f64::ceil(old_vol + (self.scroll_step.get() as f64)); let new_vol = f64::ceil(old_vol + (self.scroll_step.get() as f64));
debug!( debug!("Increase vol on card {:?} and chan {:?} by {:?} to {:?}",
"Increase vol on card {:?} and chan {:?} by {:?} to {:?}", self.acard
self.acard.borrow().card_name().unwrap(), .borrow()
self.acard.borrow().chan_name().unwrap(), .card_name()
(new_vol - old_vol), .unwrap(),
new_vol self.acard
); .borrow()
.chan_name()
.unwrap(),
(new_vol - old_vol),
new_vol);
self.set_vol(new_vol, user)?; self.set_vol(new_vol, user)?;
invoke_handlers( invoke_handlers(&self.handlers.borrow(),
&self.handlers.borrow(), AudioSignal::ValuesChanged,
AudioSignal::ValuesChanged, user);
user,
);
return Ok(()); return Ok(());
} }
@ -220,21 +231,23 @@ impl Audio {
let old_vol = self.vol()?; let old_vol = self.vol()?;
let new_vol = old_vol - (self.scroll_step.get() as f64); let new_vol = old_vol - (self.scroll_step.get() as f64);
debug!( debug!("Decrease vol on card {:?} and chan {:?} by {:?} to {:?}",
"Decrease vol on card {:?} and chan {:?} by {:?} to {:?}", self.acard
self.acard.borrow().card_name().unwrap(), .borrow()
self.acard.borrow().chan_name().unwrap(), .card_name()
(new_vol - old_vol), .unwrap(),
new_vol self.acard
); .borrow()
.chan_name()
.unwrap(),
(new_vol - old_vol),
new_vol);
self.set_vol(new_vol, user)?; self.set_vol(new_vol, user)?;
invoke_handlers( invoke_handlers(&self.handlers.borrow(),
&self.handlers.borrow(), AudioSignal::ValuesChanged,
AudioSignal::ValuesChanged, user);
user,
);
return Ok(()); return Ok(());
} }
@ -253,21 +266,25 @@ impl Audio {
let mut rc = self.last_action_timestamp.borrow_mut(); let mut rc = self.last_action_timestamp.borrow_mut();
*rc = glib::get_monotonic_time(); *rc = glib::get_monotonic_time();
debug!( debug!("Setting mute to {} on card {:?} and chan {:?} by user {:?}",
"Setting mute to {} on card {:?} and chan {:?} by user {:?}", mute,
mute, self.acard
self.acard.borrow().card_name().unwrap(), .borrow()
self.acard.borrow().chan_name().unwrap(), .card_name()
user .unwrap(),
); self.acard
.borrow()
.chan_name()
.unwrap(),
user);
self.acard.borrow().set_mute(mute)?; self.acard
.borrow()
.set_mute(mute)?;
invoke_handlers( invoke_handlers(&self.handlers.borrow(),
&self.handlers.borrow(), AudioSignal::ValuesChanged,
AudioSignal::ValuesChanged, user);
user,
);
return Ok(()); return Ok(());
} }
@ -284,16 +301,12 @@ impl Audio {
} }
fn invoke_handlers( fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>, signal: AudioSignal,
signal: AudioSignal, user: AudioUser) {
user: AudioUser, debug!("Invoking handlers for signal {:?} by user {:?}",
) { signal,
debug!( user);
"Invoking handlers for signal {:?} by user {:?}",
signal,
user
);
for handler in handlers { for handler in handlers {
let unboxed = handler.as_ref(); let unboxed = handler.as_ref();
unboxed(signal, user); unboxed(signal, user);
@ -301,11 +314,9 @@ fn invoke_handlers(
} }
fn on_alsa_event( fn on_alsa_event(last_action_timestamp: &mut i64,
last_action_timestamp: &mut i64, handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>, alsa_event: AlsaEvent) {
alsa_event: AlsaEvent,
) {
let last: i64 = *last_action_timestamp; let last: i64 = *last_action_timestamp;
if last != 0 { if last != 0 {
@ -325,11 +336,9 @@ fn on_alsa_event(
AlsaEvent::AlsaCardDiconnected => debug!("AlsaCardDiconnected"), AlsaEvent::AlsaCardDiconnected => debug!("AlsaCardDiconnected"),
AlsaEvent::AlsaCardValuesChanged => { AlsaEvent::AlsaCardValuesChanged => {
debug!("AlsaCardValuesChanged"); debug!("AlsaCardValuesChanged");
invoke_handlers( invoke_handlers(handlers,
handlers, self::AudioSignal::ValuesChanged,
self::AudioSignal::ValuesChanged, self::AudioUser::Unknown);
self::AudioUser::Unknown,
);
} }
e => warn!("Unhandled alsa event: {:?}", e), e => warn!("Unhandled alsa event: {:?}", e),
} }

View File

@ -62,16 +62,16 @@ impl From<MiddleClickAction> for i32 {
#[serde(default)] #[serde(default)]
pub struct DevicePrefs { pub struct DevicePrefs {
pub card: String, pub card: String,
pub channel: String, pub channel: String,
// TODO: normalize volume? // TODO: normalize volume?
} }
impl Default for DevicePrefs { impl Default for DevicePrefs {
fn default() -> DevicePrefs { fn default() -> DevicePrefs {
return DevicePrefs { return DevicePrefs {
card: String::from("(default)"), card: String::from("(default)"),
channel: String::from("Master"), channel: String::from("Master"),
}; };
} }
} }
@ -82,18 +82,18 @@ pub struct ViewPrefs {
pub draw_vol_meter: bool, pub draw_vol_meter: bool,
pub vol_meter_offset: i32, pub vol_meter_offset: i32,
pub system_theme: bool, pub system_theme: bool,
pub vol_meter_color: VolColor, pub vol_meter_color: VolColor,
// TODO: Display text folume/text volume pos? // TODO: Display text folume/text volume pos?
} }
impl Default for ViewPrefs { impl Default for ViewPrefs {
fn default() -> ViewPrefs { fn default() -> ViewPrefs {
return ViewPrefs { return ViewPrefs {
draw_vol_meter: true, draw_vol_meter: true,
vol_meter_offset: 10, vol_meter_offset: 10,
system_theme: true, system_theme: true,
vol_meter_color: VolColor::default(), vol_meter_color: VolColor::default(),
}; };
} }
} }
@ -109,10 +109,10 @@ pub struct VolColor {
impl Default for VolColor { impl Default for VolColor {
fn default() -> VolColor { fn default() -> VolColor {
return VolColor { return VolColor {
red: 245, red: 245,
green: 180, green: 180,
blue: 0, blue: 0,
}; };
} }
} }
@ -129,11 +129,11 @@ pub struct BehaviorPrefs {
impl Default for BehaviorPrefs { impl Default for BehaviorPrefs {
fn default() -> BehaviorPrefs { fn default() -> BehaviorPrefs {
return BehaviorPrefs { return BehaviorPrefs {
vol_control_cmd: None, vol_control_cmd: None,
vol_scroll_step: 5.0, vol_scroll_step: 5.0,
middle_click_action: MiddleClickAction::default(), middle_click_action: MiddleClickAction::default(),
custom_command: None, custom_command: None,
}; };
} }
} }
@ -145,19 +145,19 @@ pub struct NotifyPrefs {
pub notifcation_timeout: i64, pub notifcation_timeout: i64,
pub notify_mouse_scroll: bool, pub notify_mouse_scroll: bool,
pub notify_popup: bool, pub notify_popup: bool,
pub notify_external: bool, pub notify_external: bool,
// TODO: notify_hotkeys? // TODO: notify_hotkeys?
} }
impl Default for NotifyPrefs { impl Default for NotifyPrefs {
fn default() -> NotifyPrefs { fn default() -> NotifyPrefs {
return NotifyPrefs { return NotifyPrefs {
enable_notifications: true, enable_notifications: true,
notifcation_timeout: 1500, notifcation_timeout: 1500,
notify_mouse_scroll: true, notify_mouse_scroll: true,
notify_popup: true, notify_popup: true,
notify_external: true, notify_external: true,
}; };
} }
} }
@ -168,7 +168,7 @@ pub struct Prefs {
pub device_prefs: DevicePrefs, pub device_prefs: DevicePrefs,
pub view_prefs: ViewPrefs, pub view_prefs: ViewPrefs,
pub behavior_prefs: BehaviorPrefs, pub behavior_prefs: BehaviorPrefs,
pub notify_prefs: NotifyPrefs, pub notify_prefs: NotifyPrefs,
// TODO: HotKeys? // TODO: HotKeys?
} }
@ -211,8 +211,8 @@ impl Prefs {
pub fn store_config(&self) -> Result<()> { pub fn store_config(&self) -> Result<()> {
let config_path = let config_path = get_xdg_dirs().place_config_file("pnmixer.toml")
get_xdg_dirs().place_config_file("pnmixer.toml").from_err()?; .from_err()?;
debug!("Storing config in {:?}", config_path); debug!("Storing config in {:?}", config_path);
@ -245,10 +245,9 @@ impl Prefs {
} }
impl Display for Prefs { impl Display for Prefs {
fn fmt( fn fmt(&self,
&self, f: &mut Formatter)
f: &mut Formatter, -> std::result::Result<(), std::fmt::Error> {
) -> std::result::Result<(), std::fmt::Error> {
let s = self.to_str(); let s = self.to_str();
return write!(f, "{}", s); return write!(f, "{}", s);
} }

View File

@ -81,7 +81,9 @@ pub fn get_selem_names(mixer: &Mixer) -> Vec<String> {
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))?;
if n == name { if n == name {
return Ok(selem); return Ok(selem);

26
src/support_cmd.rs Normal file
View File

@ -0,0 +1,26 @@
use errors::*;
use glib;
use prefs::Prefs;
use std::error::Error;
use std;
pub fn execute_vol_control_command(prefs: &Prefs) -> Result<()> {
let m_cmd = prefs.get_avail_vol_control_cmd();
match m_cmd {
Some(ref cmd) => execute_command(cmd.as_str()),
None => bail!("No command found"),
}
}
pub fn execute_command(cmd: &str) -> Result<()> {
return glib::spawn_command_line_async(cmd)
.map_err(|e| {
std::io::Error::new(std::io::ErrorKind::Other,
e.description())
})
.from_err();
}

View File

@ -21,21 +21,18 @@ pub fn copy_pixbuf(pixbuf: &gdk_pixbuf::Pixbuf) -> gdk_pixbuf::Pixbuf {
} }
pub fn pixbuf_new_from_theme( pub fn pixbuf_new_from_theme(icon_name: &str,
icon_name: &str, size: i32,
size: i32, theme: &gtk::IconTheme)
theme: &gtk::IconTheme, -> Result<gdk_pixbuf::Pixbuf> {
) -> Result<gdk_pixbuf::Pixbuf> {
let icon_info = theme let icon_info =
.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty()) theme.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty())
.ok_or(format!("Couldn't find icon {}", icon_name))?; .ok_or(format!("Couldn't find icon {}", icon_name))?;
debug!( debug!("Loading stock icon {} from {:?}",
"Loading stock icon {} from {:?}", icon_name,
icon_name, icon_info.get_filename().unwrap_or(PathBuf::new()));
icon_info.get_filename().unwrap_or(PathBuf::new())
);
// TODO: propagate error // TODO: propagate error
let pixbuf = icon_info.load_icon().unwrap(); let pixbuf = icon_info.load_icon().unwrap();
@ -47,7 +44,7 @@ pub fn pixbuf_new_from_theme(
pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> { pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> {
ensure!(!filename.is_empty(), "Filename is empty"); ensure!(!filename.is_empty(), "Filename is empty");
let s = format!("./data/pixmaps/{}", filename); let s = format!("{}/data/pixmaps/{}", env!("CARGO_MANIFEST_DIR"), filename);
let path = Path::new(s.as_str()); let path = Path::new(s.as_str());
if path.exists() { if path.exists() {

View File

@ -20,18 +20,17 @@ pub struct Gui {
} }
impl Gui { impl Gui {
pub fn new( pub fn new(builder_popup_window: gtk::Builder,
builder_popup_window: gtk::Builder, builder_popup_menu: gtk::Builder,
builder_popup_menu: gtk::Builder, prefs: &Prefs)
prefs: &Prefs, -> Gui {
) -> Gui {
return Gui { return Gui {
_cant_construct: (), _cant_construct: (),
tray_icon: TrayIcon::new(prefs).unwrap(), tray_icon: TrayIcon::new(prefs).unwrap(),
popup_window: PopupWindow::new(builder_popup_window), popup_window: PopupWindow::new(builder_popup_window),
popup_menu: PopupMenu::new(builder_popup_menu), popup_menu: PopupMenu::new(builder_popup_menu),
prefs_dialog: RefCell::new(None), prefs_dialog: RefCell::new(None),
}; };
} }
} }

View File

@ -11,19 +11,17 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION");
create_builder_item!( create_builder_item!(PopupMenu,
PopupMenu, menu_window: gtk::Window,
menu_window: gtk::Window, menubar: gtk::MenuBar,
menubar: gtk::MenuBar, menu: gtk::Menu,
menu: gtk::Menu, about_item: gtk::MenuItem,
about_item: gtk::MenuItem, mixer_item: gtk::MenuItem,
mixer_item: gtk::MenuItem, mute_item: gtk::MenuItem,
mute_item: gtk::MenuItem, mute_check: gtk::CheckButton,
mute_check: gtk::CheckButton, prefs_item: gtk::MenuItem,
prefs_item: gtk::MenuItem, quit_item: gtk::MenuItem,
quit_item: gtk::MenuItem, reload_item: gtk::MenuItem);
reload_item: gtk::MenuItem
);
@ -33,15 +31,20 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
let apps = appstate.clone(); let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| { appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if window is hidden */ /* skip if window is hidden */
if !apps.gui.popup_menu.menu.get_visible() { if !apps.gui
.popup_menu
.menu
.get_visible() {
return; return;
} }
match (s, u) { match (s, u) {
(AudioSignal::ValuesChanged, _) => { (_, _) => {
let muted = try_w!(apps.audio.get_mute()); let muted = try_w!(apps.audio.get_mute());
apps.gui.popup_menu.mute_check.set_active(muted); apps.gui
.popup_menu
.mute_check
.set_active(muted);
} }
_ => (),
} }
})); }));
@ -50,10 +53,16 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
/* popup_menu.menu.connect_show */ /* popup_menu.menu.connect_show */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
appstate.gui.popup_menu.menu.connect_show(move |_| { appstate.gui
let muted = try_w!(apps.audio.get_mute()); .popup_menu
apps.gui.popup_menu.mute_check.set_active(muted); .menu
}); .connect_show(move |_| {
let muted = try_w!(apps.audio.get_mute());
apps.gui
.popup_menu
.mute_check
.set_active(muted);
});
} }
@ -79,18 +88,27 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
{ {
let apps = appstate.clone(); let apps = appstate.clone();
let about_item = &appstate.gui.popup_menu.about_item; let about_item = &appstate.gui.popup_menu.about_item;
about_item.connect_activate( about_item.connect_activate(move |_| {
move |_| { on_about_item_activate(&apps); }, on_about_item_activate(&apps);
); });
} }
/* about_item.connect_activate_link */ /* about_item.connect_activate_link */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
let prefs_item = &appstate.gui.popup_menu.prefs_item; let prefs_item = &appstate.gui.popup_menu.prefs_item;
prefs_item.connect_activate( prefs_item.connect_activate(move |_| {
move |_| { on_prefs_item_activate(&apps); }, on_prefs_item_activate(&apps);
); });
}
/* quit_item.connect_activate_link */
{
let quit_item = &appstate.gui.popup_menu.quit_item;
quit_item.connect_activate(|_| {
gtk::main_quit();
});
} }
} }
@ -139,4 +157,3 @@ fn on_prefs_item_activate(appstate: &Rc<AppS>) {
/* TODO: only create if needed */ /* TODO: only create if needed */
show_prefs_dialog(appstate); show_prefs_dialog(appstate);
} }

View File

@ -28,14 +28,14 @@ pub struct PopupWindow {
impl PopupWindow { impl PopupWindow {
pub fn new(builder: gtk::Builder) -> PopupWindow { pub fn new(builder: gtk::Builder) -> PopupWindow {
return PopupWindow { return PopupWindow {
_cant_construct: (), _cant_construct: (),
popup_window: builder.get_object("popup_window").unwrap(), popup_window: builder.get_object("popup_window").unwrap(),
vol_scale_adj: builder.get_object("vol_scale_adj").unwrap(), vol_scale_adj: builder.get_object("vol_scale_adj").unwrap(),
vol_scale: builder.get_object("vol_scale").unwrap(), vol_scale: builder.get_object("vol_scale").unwrap(),
mute_check: builder.get_object("mute_check").unwrap(), mute_check: builder.get_object("mute_check").unwrap(),
mixer_button: builder.get_object("mixer_button").unwrap(), mixer_button: builder.get_object("mixer_button").unwrap(),
toggle_signal: Cell::new(0), toggle_signal: Cell::new(0),
}; };
} }
pub fn update(&self, audio: &Audio) -> Result<()> { pub fn update(&self, audio: &Audio) -> Result<()> {
@ -67,10 +67,8 @@ impl PopupWindow {
} }
} }
glib::signal_handler_unblock( glib::signal_handler_unblock(&self.mute_check,
&self.mute_check, self.toggle_signal.get());
self.toggle_signal.get(),
);
} }
} }
@ -82,7 +80,10 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
let apps = appstate.clone(); let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| { appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if window is hidden */ /* skip if window is hidden */
if !apps.gui.popup_window.popup_window.get_visible() { if !apps.gui
.popup_window
.popup_window
.get_visible() {
return; return;
} }
match (s, u) { match (s, u) {
@ -93,14 +94,13 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
* the slider value reflects the value set by user, * the slider value reflects the value set by user,
* and not the real value reported by the audio system. * and not the real value reported by the audio system.
*/ */
(AudioSignal::ValuesChanged, AudioUser::Popup) => { (_, AudioUser::Popup) => {
apps.gui.popup_window.update_mute_check(&apps.audio); apps.gui.popup_window.update_mute_check(&apps.audio);
} }
/* external change, safe to update slider too */ /* external change, safe to update slider too */
(AudioSignal::ValuesChanged, _) => { (_, _) => {
try_w!(apps.gui.popup_window.update(&apps.audio)); try_w!(apps.gui.popup_window.update(&apps.audio));
} }
_ => (),
} }
})); }));
@ -109,24 +109,37 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
/* mute_check.connect_toggled */ /* mute_check.connect_toggled */
{ {
let _appstate = appstate.clone(); let _appstate = appstate.clone();
let mute_check = &appstate.clone().gui.popup_window.mute_check; let mute_check = &appstate.clone()
let toggle_signal = mute_check.connect_toggled(move |_| { .gui
on_mute_check_toggled(&_appstate) .popup_window
}); .mute_check;
appstate.gui.popup_window.toggle_signal.set(toggle_signal); let toggle_signal =
mute_check.connect_toggled(move |_| {
on_mute_check_toggled(&_appstate)
});
appstate.gui
.popup_window
.toggle_signal
.set(toggle_signal);
} }
/* popup_window.connect_show */ /* popup_window.connect_show */
{ {
let _appstate = appstate.clone(); let _appstate = appstate.clone();
let popup_window = &appstate.clone().gui.popup_window.popup_window; let popup_window = &appstate.clone()
.gui
.popup_window
.popup_window;
popup_window.connect_show(move |_| on_popup_window_show(&_appstate)); popup_window.connect_show(move |_| on_popup_window_show(&_appstate));
} }
/* vol_scale_adj.connect_value_changed */ /* vol_scale_adj.connect_value_changed */
{ {
let _appstate = appstate.clone(); let _appstate = appstate.clone();
let vol_scale_adj = &appstate.clone().gui.popup_window.vol_scale_adj; let vol_scale_adj = &appstate.clone()
.gui
.popup_window
.vol_scale_adj;
vol_scale_adj.connect_value_changed( vol_scale_adj.connect_value_changed(
move |_| on_vol_scale_value_changed(&_appstate), move |_| on_vol_scale_value_changed(&_appstate),
); );
@ -134,16 +147,25 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
/* popup_window.connect_event */ /* popup_window.connect_event */
{ {
let popup_window = &appstate.clone().gui.popup_window.popup_window; let popup_window = &appstate.clone()
.gui
.popup_window
.popup_window;
popup_window.connect_event(move |w, e| on_popup_window_event(w, e)); popup_window.connect_event(move |w, e| on_popup_window_event(w, e));
} }
/* mixer_button.connect_clicked */ /* mixer_button.connect_clicked */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
let mixer_button = &appstate.clone().gui.popup_window.mixer_button; let mixer_button = &appstate.clone()
.gui
.popup_window
.mixer_button;
mixer_button.connect_clicked(move |_| { mixer_button.connect_clicked(move |_| {
apps.gui.popup_window.popup_window.hide(); apps.gui
.popup_window
.popup_window
.hide();
try_w!(execute_vol_control_command(&apps.prefs.borrow())); try_w!(execute_vol_control_command(&apps.prefs.borrow()));
}); });
} }
@ -152,7 +174,10 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
fn on_popup_window_show(appstate: &AppS) { fn on_popup_window_show(appstate: &AppS) {
try_w!(appstate.gui.popup_window.update(&appstate.audio)); try_w!(appstate.gui.popup_window.update(&appstate.audio));
appstate.gui.popup_window.vol_scale.grab_focus(); appstate.gui
.popup_window
.vol_scale
.grab_focus();
try_w!(grab_devices(&appstate.gui.popup_window.popup_window)); try_w!(grab_devices(&appstate.gui.popup_window.popup_window));
} }
@ -189,7 +214,10 @@ fn on_popup_window_event(w: &gtk::Window, e: &gdk::Event) -> gtk::Inhibit {
fn on_vol_scale_value_changed(appstate: &AppS) { fn on_vol_scale_value_changed(appstate: &AppS) {
let audio = &appstate.audio; let audio = &appstate.audio;
let val = appstate.gui.popup_window.vol_scale.get_value(); let val = appstate.gui
.popup_window
.vol_scale
.get_value();
try_w!(audio.set_vol(val, AudioUser::Popup)); try_w!(audio.set_vol(val, AudioUser::Popup));
} }
@ -212,40 +240,32 @@ pub fn grab_devices(window: &gtk::Window) -> Result<()> {
let gdk_window = window.get_window().ok_or("No window?!")?; let gdk_window = window.get_window().ok_or("No window?!")?;
/* Grab the mouse */ /* Grab the mouse */
let m_grab_status = device.grab( let m_grab_status =
&gdk_window, device.grab(&gdk_window,
GrabOwnership::None, GrabOwnership::None,
true, true,
BUTTON_PRESS_MASK, BUTTON_PRESS_MASK,
None, None,
GDK_CURRENT_TIME as u32, GDK_CURRENT_TIME as u32);
);
if m_grab_status != GrabStatus::Success { if m_grab_status != GrabStatus::Success {
warn!( warn!("Could not grab {}",
"Could not grab {}", device.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
} }
/* Grab the keyboard */ /* Grab the keyboard */
let k_dev = device.get_associated_device().ok_or( let k_dev = device.get_associated_device()
"Couldn't get associated device", .ok_or("Couldn't get associated device")?;
)?;
let k_grab_status = k_dev.grab( let k_grab_status = k_dev.grab(&gdk_window,
&gdk_window, GrabOwnership::None,
GrabOwnership::None, true,
true, KEY_PRESS_MASK,
KEY_PRESS_MASK, None,
None, GDK_CURRENT_TIME as u32);
GDK_CURRENT_TIME as u32,
);
if k_grab_status != GrabStatus::Success { if k_grab_status != GrabStatus::Success {
warn!( warn!("Could not grab {}",
"Could not grab {}", k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
);
} }
return Ok(()); return Ok(());

View File

@ -1,5 +1,5 @@
use app_state::*; use app_state::*;
use audio::AudioUser; use audio::{AudioUser, AudioSignal};
use gdk; use gdk;
use gtk::ResponseType; use gtk::ResponseType;
use gtk::prelude::*; use gtk::prelude::*;
@ -7,6 +7,7 @@ use gtk;
use prefs::*; use prefs::*;
use std::rc::Rc; use std::rc::Rc;
use support_alsa::*; use support_alsa::*;
use errors::*;
@ -45,9 +46,7 @@ pub struct PrefsDialog {
impl PrefsDialog { impl PrefsDialog {
pub fn new() -> PrefsDialog { pub fn new() -> PrefsDialog {
let builder = gtk::Builder::new_from_string( let builder = gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/ui/prefs-dialog.glade")));
include_str!("../data/ui/prefs-dialog.glade"),
);
let prefs_dialog = PrefsDialog { let prefs_dialog = PrefsDialog {
_cant_construct: (), _cant_construct: (),
prefs_dialog: builder.get_object("prefs_dialog").unwrap(), prefs_dialog: builder.get_object("prefs_dialog").unwrap(),
@ -55,21 +54,17 @@ impl PrefsDialog {
card_combo: builder.get_object("card_combo").unwrap(), card_combo: builder.get_object("card_combo").unwrap(),
chan_combo: builder.get_object("chan_combo").unwrap(), chan_combo: builder.get_object("chan_combo").unwrap(),
vol_meter_draw_check: builder vol_meter_draw_check: builder.get_object("vol_meter_draw_check")
.get_object("vol_meter_draw_check")
.unwrap(), .unwrap(),
vol_meter_pos_spin: builder vol_meter_pos_spin: builder.get_object("vol_meter_pos_spin")
.get_object("vol_meter_pos_spin")
.unwrap(), .unwrap(),
vol_meter_color_button: builder vol_meter_color_button: builder.get_object("vol_meter_color_button")
.get_object("vol_meter_color_button")
.unwrap(), .unwrap(),
system_theme: builder.get_object("system_theme").unwrap(), system_theme: builder.get_object("system_theme").unwrap(),
vol_control_entry: builder.get_object("vol_control_entry").unwrap(), vol_control_entry: builder.get_object("vol_control_entry").unwrap(),
scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(), scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(),
middle_click_combo: builder middle_click_combo: builder.get_object("middle_click_combo")
.get_object("middle_click_combo")
.unwrap(), .unwrap(),
custom_entry: builder.get_object("custom_entry").unwrap(), custom_entry: builder.get_object("custom_entry").unwrap(),
@ -88,26 +83,14 @@ impl PrefsDialog {
pub fn from_prefs(&self, prefs: &Prefs) { pub fn from_prefs(&self, prefs: &Prefs) {
/* DevicePrefs */ /* DevicePrefs */
/* filled on show signal with audio info */
self.card_combo.remove_all(); self.card_combo.remove_all();
self.card_combo.append_text(
prefs.device_prefs.card.as_str(),
);
self.card_combo.set_active(0);
self.chan_combo.remove_all(); self.chan_combo.remove_all();
self.chan_combo.append_text(
prefs.device_prefs.channel.as_str(),
);
self.chan_combo.set_active(0);
/* ViewPrefs */ /* ViewPrefs */
self.vol_meter_draw_check.set_active( self.vol_meter_draw_check.set_active(prefs.view_prefs.draw_vol_meter);
prefs.view_prefs.draw_vol_meter, self.vol_meter_pos_spin.set_value(prefs.view_prefs.vol_meter_offset as
); f64);
self.vol_meter_pos_spin.set_value(
prefs.view_prefs.vol_meter_offset as
f64,
);
// TODO don't convert like that // TODO don't convert like that
let rgba = gdk::RGBA { let rgba = gdk::RGBA {
@ -120,17 +103,12 @@ impl PrefsDialog {
self.system_theme.set_active(prefs.view_prefs.system_theme); self.system_theme.set_active(prefs.view_prefs.system_theme);
/* BehaviorPrefs */ /* BehaviorPrefs */
self.vol_control_entry.set_text( self.vol_control_entry.set_text(prefs.behavior_prefs
prefs .vol_control_cmd
.behavior_prefs .as_ref()
.vol_control_cmd .unwrap_or(&String::from(""))
.as_ref() .as_str());
.unwrap_or(&String::from("")) self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step);
.as_str(),
);
self.scroll_step_spin.set_value(
prefs.behavior_prefs.vol_scroll_step,
);
// TODO: make sure these values always match, must be a better way // TODO: make sure these values always match, must be a better way
// also check to_prefs() // also check to_prefs()
@ -138,53 +116,38 @@ impl PrefsDialog {
self.middle_click_combo.append_text("Show Preferences"); self.middle_click_combo.append_text("Show Preferences");
self.middle_click_combo.append_text("Volume Control"); self.middle_click_combo.append_text("Volume Control");
self.middle_click_combo.append_text("Custom Command (set below)"); self.middle_click_combo.append_text("Custom Command (set below)");
self.middle_click_combo.set_active( self.middle_click_combo.set_active(prefs.behavior_prefs
prefs .middle_click_action
.behavior_prefs .into());
.middle_click_action self.custom_entry.set_text(prefs.behavior_prefs
.into(), .custom_command
); .as_ref()
self.custom_entry.set_text( .unwrap_or(&String::from(""))
prefs .as_str());
.behavior_prefs
.custom_command
.as_ref()
.unwrap_or(&String::from(""))
.as_str(),
);
/* NotifyPrefs */ /* NotifyPrefs */
self.noti_enable_check.set_active( self.noti_enable_check
prefs .set_active(prefs.notify_prefs.enable_notifications);
.notify_prefs self.noti_timeout_spin
.enable_notifications, .set_value(prefs.notify_prefs.notifcation_timeout as f64);
); self.noti_mouse_check
self.noti_timeout_spin.set_value( .set_active(prefs.notify_prefs.notify_mouse_scroll);
prefs.notify_prefs.notifcation_timeout as self.noti_popup_check.set_active(prefs.notify_prefs.notify_popup);
f64, self.noti_ext_check.set_active(prefs.notify_prefs.notify_external);
);
self.noti_mouse_check.set_active(
prefs.notify_prefs.notify_mouse_scroll,
);
self.noti_popup_check.set_active(
prefs.notify_prefs.notify_popup,
);
self.noti_ext_check.set_active(
prefs.notify_prefs.notify_external,
);
} }
pub fn to_prefs(&self) -> Prefs { pub fn to_prefs(&self) -> Prefs {
// TODO: remove duplication with default instance // TODO: remove duplication with default instance
let device_prefs = DevicePrefs { let device_prefs =
card: self.card_combo.get_active_text().unwrap_or( DevicePrefs {
String::from("(default)"), card: self.card_combo
), .get_active_text()
channel: self.chan_combo.get_active_text().unwrap_or( .unwrap_or(String::from("(default)")),
String::from("Master"), channel: self.chan_combo
), .get_active_text()
}; .unwrap_or(String::from("Master")),
};
// TODO don't convert like that // TODO don't convert like that
let vol_meter_color = VolColor { let vol_meter_color = VolColor {
@ -200,16 +163,19 @@ impl PrefsDialog {
vol_meter_color, vol_meter_color,
}; };
let vol_control_cmd = self.vol_control_entry.get_text().and_then(|x| { let vol_control_cmd =
if x.is_empty() { None } else { Some(x) } self.vol_control_entry.get_text().and_then(|x| if x.is_empty() {
}); None
} else {
Some(x)
});
let custom_command = let custom_command =
self.custom_entry.get_text().and_then(|x| if x.is_empty() { self.custom_entry.get_text().and_then(|x| if x.is_empty() {
None None
} else { } else {
Some(x) Some(x)
}); });
let behavior_prefs = BehaviorPrefs { let behavior_prefs = BehaviorPrefs {
vol_control_cmd, vol_control_cmd,
@ -221,25 +187,28 @@ impl PrefsDialog {
let notify_prefs = NotifyPrefs { let notify_prefs = NotifyPrefs {
enable_notifications: self.noti_enable_check.get_active(), enable_notifications: self.noti_enable_check.get_active(),
notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as
i64, i64,
notify_mouse_scroll: self.noti_mouse_check.get_active(), notify_mouse_scroll: self.noti_mouse_check.get_active(),
notify_popup: self.noti_popup_check.get_active(), notify_popup: self.noti_popup_check.get_active(),
notify_external: self.noti_ext_check.get_active(), notify_external: self.noti_ext_check.get_active(),
}; };
return Prefs { return Prefs {
device_prefs, device_prefs,
view_prefs, view_prefs,
behavior_prefs, behavior_prefs,
notify_prefs, notify_prefs,
}; };
} }
} }
pub fn show_prefs_dialog(appstate: &Rc<AppS>) { pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
if appstate.gui.prefs_dialog.borrow().is_some() { if appstate.gui
.prefs_dialog
.borrow()
.is_some() {
return; return;
} }
@ -260,6 +229,22 @@ pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
/* TODO: do the references get dropped when the dialog window is gone? */ /* TODO: do the references get dropped when the dialog window is gone? */
pub fn init_prefs_dialog(appstate: &Rc<AppS>) { pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
/* audio.connect_handler */
{
let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| {
match (s, u) {
(AudioSignal::CardInitialized, _) => (),
(AudioSignal::CardCleanedUp, _) => {
fill_card_combo(&apps);
fill_chan_combo(&apps, None);
},
_ => (),
}
}));
}
/* prefs_dialog.connect_show */ /* prefs_dialog.connect_show */
{ {
@ -267,11 +252,30 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
let m_pd = appstate.gui.prefs_dialog.borrow(); let m_pd = appstate.gui.prefs_dialog.borrow();
let pd = m_pd.as_ref().unwrap(); let pd = m_pd.as_ref().unwrap();
pd.prefs_dialog.connect_show( pd.prefs_dialog.connect_show(
move |_| { on_prefs_dialog_show(&apps); }, move |_| {
fill_card_combo(&apps);
fill_chan_combo(&apps, None);
}
); );
} }
/* prefs_dialog.connect_show */ /* card_combo.connect_changed */
{
let apps = appstate.clone();
let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
card_combo.connect_changed(move |_| {
let m_cc = apps.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
let card_name = card_combo
.get_active_text().unwrap();
fill_chan_combo(&apps, Some(card_name));
return;
});
}
/* prefs_dialog.connect_response */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
let m_pd = appstate.gui.prefs_dialog.borrow(); let m_pd = appstate.gui.prefs_dialog.borrow();
@ -298,47 +302,33 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
// TODO: update popup, tray_icon, hotkeys, notification and audio // TODO: update popup, tray_icon, hotkeys, notification and audio
try_w!(apps.update_tray_icon()); try_w!(apps.update_tray_icon());
try_w!(apps.update_popup_window()); try_w!(apps.update_popup_window());
{
let prefs = apps.prefs.borrow();
let card = &prefs.device_prefs.card;
let channel = &prefs.device_prefs.channel;
try_w!(apps.audio.switch_acard(
Some(card.clone()),
Some(channel.clone()),
AudioUser::PrefsWindow));
}
let prefs = apps.prefs.borrow_mut(); let prefs = apps.prefs.borrow_mut();
try_w!(prefs.store_config()); try_w!(prefs.store_config());
} }
}); });
} }
// TODO: fix combo box behavior and filling
/* DEVICE TAB */
/* card_combo.connect_changed */
{
let apps = appstate.clone();
let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
// TODO: refill channel combo
card_combo.connect_changed(move |_| { on_card_combo_changed(&apps); });
}
/* card_combo.connect_changed */
{
let apps = appstate.clone();
let m_cc = appstate.gui.prefs_dialog.borrow();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
chan_combo.connect_changed(move |_| { on_chan_combo_changed(&apps); });
}
} }
fn on_prefs_dialog_show(appstate: &AppS) { fn fill_card_combo(appstate: &AppS) {
let m_cc = appstate.gui.prefs_dialog.borrow(); let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo; let card_combo = &m_cc.as_ref().unwrap().card_combo;
let m_cc = appstate.gui.prefs_dialog.borrow(); card_combo.remove_all();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
let acard = appstate.audio.acard.borrow(); let acard = appstate.audio.acard.borrow();
/* set card combo */ /* set card combo */
let cur_card_name = let cur_card_name = try_w!(acard.card_name(),
try_w!(acard.card_name(), "Can't get current card name!"); "Can't get current card name!");
let available_card_names = get_alsa_card_names(); let available_card_names = get_alsa_card_names();
/* set_active_id doesn't work, so save the index */ /* set_active_id doesn't work, so save the index */
@ -353,12 +343,26 @@ fn on_prefs_dialog_show(appstate: &AppS) {
// TODO, block signal? // TODO, block signal?
card_combo.set_active(c_index); card_combo.set_active(c_index);
}
fn fill_chan_combo(appstate: &AppS, cardname: Option<String>) {
let m_cc = appstate.gui.prefs_dialog.borrow();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
chan_combo.remove_all();
let cur_acard = appstate.audio.acard.borrow();
let card = match cardname {
Some(name) => {
try_w!(get_alsa_card_by_name(name).from_err())
},
None => cur_acard.as_ref().card,
};
/* set chan combo */ /* set chan combo */
let cur_chan_name = try_w!(acard.chan_name()); let cur_chan_name = try_w!(cur_acard.chan_name());
let available_chan_names = get_selem_names(&acard.mixer); let mixer = try_w!(get_mixer(&card));
let available_chan_names = get_selem_names(&mixer);
/* set_active_id doesn't work, so save the index */ /* set_active_id doesn't work, so save the index */
let mut c_index: i32 = -1; let mut c_index: i32 = -1;
@ -375,53 +379,3 @@ fn on_prefs_dialog_show(appstate: &AppS) {
} }
fn on_card_combo_changed(appstate: &AppS) {
let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
let m_cc = appstate.gui.prefs_dialog.borrow();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
let active_card_item = try_w!(card_combo.get_active_text().ok_or(
"No active Card item found",
));
let active_chan_item = chan_combo.get_active_id();
let cur_card_name = {
let acard = appstate.audio.acard.borrow();
try_w!(acard.card_name(), "Can't get current card name!")
};
if active_card_item != cur_card_name {
appstate.audio.switch_acard(
Some(cur_card_name),
active_chan_item,
AudioUser::PrefsWindow,
);
}
}
fn on_chan_combo_changed(appstate: &AppS) {
let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
let m_cc = appstate.gui.prefs_dialog.borrow();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
let active_chan_item = try_w!(chan_combo.get_active_text().ok_or(
"No active Chan item found",
));
let cur_card_name = {
let acard = appstate.audio.acard.borrow();
acard.card_name().ok()
};
let cur_chan_name = {
let acard = appstate.audio.acard.borrow();
try_w!(acard.chan_name())
};
if active_chan_item != cur_chan_name {
appstate.audio.switch_acard(
cur_card_name,
Some(active_chan_item),
AudioUser::PrefsWindow,
);
}
}

View File

@ -45,12 +45,12 @@ impl TrayIcon {
let status_icon = gtk::StatusIcon::new(); let status_icon = gtk::StatusIcon::new();
return Ok(TrayIcon { return Ok(TrayIcon {
_cant_construct: (), _cant_construct: (),
volmeter, volmeter,
audio_pix: RefCell::new(audio_pix), audio_pix: RefCell::new(audio_pix),
status_icon, status_icon,
icon_size: Cell::new(ICON_MIN_SIZE), icon_size: Cell::new(ICON_MIN_SIZE),
}); });
} }
@ -78,12 +78,11 @@ impl TrayIcon {
} }
pub fn update_all( pub fn update_all(&self,
&self, prefs: &Prefs,
prefs: &Prefs, audio: &Audio,
audio: &Audio, m_size: Option<i32>)
m_size: Option<i32>, -> Result<()> {
) -> Result<()> {
match m_size { match m_size {
Some(s) => { Some(s) => {
if s < ICON_MIN_SIZE { if s < ICON_MIN_SIZE {
@ -122,37 +121,30 @@ pub struct VolMeter {
impl VolMeter { impl VolMeter {
pub fn new(prefs: &Prefs) -> VolMeter { pub fn new(prefs: &Prefs) -> VolMeter {
return VolMeter { return VolMeter {
red: prefs.view_prefs.vol_meter_color.red, red: prefs.view_prefs.vol_meter_color.red,
green: prefs.view_prefs.vol_meter_color.green, green: prefs.view_prefs.vol_meter_color.green,
blue: prefs.view_prefs.vol_meter_color.blue, blue: prefs.view_prefs.vol_meter_color.blue,
x_offset_pct: prefs.view_prefs.vol_meter_offset as i64, x_offset_pct: prefs.view_prefs.vol_meter_offset as i64,
y_offset_pct: 10, y_offset_pct: 10,
/* dynamic */ /* dynamic */
width: Cell::new(0), width: Cell::new(0),
row: RefCell::new(vec![]), row: RefCell::new(vec![]),
}; };
} }
// TODO: cache input pixbuf? // TODO: cache input pixbuf?
fn meter_draw( fn meter_draw(&self,
&self, volume: i64,
volume: i64, pixbuf: &gdk_pixbuf::Pixbuf)
pixbuf: &gdk_pixbuf::Pixbuf, -> Result<gdk_pixbuf::Pixbuf> {
) -> Result<gdk_pixbuf::Pixbuf> {
ensure!( ensure!(pixbuf.get_colorspace() == GDK_COLORSPACE_RGB,
pixbuf.get_colorspace() == GDK_COLORSPACE_RGB, "Invalid colorspace in pixbuf");
"Invalid colorspace in pixbuf" ensure!(pixbuf.get_bits_per_sample() == 8,
); "Invalid bits per sample in pixbuf");
ensure!(
pixbuf.get_bits_per_sample() == 8,
"Invalid bits per sample in pixbuf"
);
ensure!(pixbuf.get_has_alpha(), "No alpha channel in pixbuf"); ensure!(pixbuf.get_has_alpha(), "No alpha channel in pixbuf");
ensure!( ensure!(pixbuf.get_n_channels() == 4,
pixbuf.get_n_channels() == 4, "Invalid number of channels in pixbuf");
"Invalid number of channels in pixbuf"
);
let i_width = pixbuf.get_width() as i64; let i_width = pixbuf.get_width() as i64;
let i_height = pixbuf.get_height() as i64; let i_height = pixbuf.get_height() as i64;
@ -161,21 +153,16 @@ impl VolMeter {
let vm_width = i_width / 6; let vm_width = i_width / 6;
let x = (self.x_offset_pct as f64 * let x = (self.x_offset_pct as f64 *
((i_width - vm_width) as f64 / 100.0)) as ((i_width - vm_width) as f64 / 100.0)) as i64;
i64; ensure!(x >= 0 && (x + vm_width) <= i_width,
ensure!( "x coordinate invalid: {}",
x >= 0 && (x + vm_width) <= i_width, x);
"x coordinate invalid: {}",
x
);
let y = (self.y_offset_pct as f64 * (i_height as f64 / 100.0)) as i64; let y = (self.y_offset_pct as f64 * (i_height as f64 / 100.0)) as i64;
let vm_height = let vm_height =
((i_height - (y * 2)) as f64 * (volume as f64 / 100.0)) as i64; ((i_height - (y * 2)) as f64 * (volume as f64 / 100.0)) as i64;
ensure!( ensure!(y >= 0 && (y + vm_height) <= i_height,
y >= 0 && (y + vm_height) <= i_height, "y coordinate invalid: {}",
"y coordinate invalid: {}", y);
y
);
/* Let's check if the icon width changed, in which case we /* Let's check if the icon width changed, in which case we
* must reinit our internal row of pixels. * must reinit our internal row of pixels.
@ -211,9 +198,8 @@ impl VolMeter {
let p_index = ((row_offset * rowstride) + col_offset) as usize; let p_index = ((row_offset * rowstride) + col_offset) as usize;
let row = self.row.borrow(); let row = self.row.borrow();
pixels[p_index..p_index + row.len()].copy_from_slice( pixels[p_index..p_index + row.len()]
row.as_ref(), .copy_from_slice(row.as_ref());
);
} }
} }
@ -311,11 +297,7 @@ impl AudioPix {
pub fn init_tray_icon(appstate: Rc<AppS>) { pub fn init_tray_icon(appstate: Rc<AppS>) {
let audio = &appstate.audio; let audio = &appstate.audio;
let tray_icon = &appstate.gui.tray_icon; let tray_icon = &appstate.gui.tray_icon;
try_e!(tray_icon.update_all( try_e!(tray_icon.update_all(&appstate.prefs.borrow_mut(), &audio, None));
&appstate.prefs.borrow_mut(),
&audio,
None,
));
tray_icon.status_icon.set_visible(true); tray_icon.status_icon.set_visible(true);
@ -324,10 +306,9 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
let apps = appstate.clone(); let apps = appstate.clone();
appstate.audio.connect_handler( appstate.audio.connect_handler(
Box::new(move |s, u| match (s, u) { Box::new(move |s, u| match (s, u) {
(AudioSignal::ValuesChanged, _) => { (_, _) => {
try_w!(apps.gui.tray_icon.update_audio(&apps.audio)); try_w!(apps.gui.tray_icon.update_audio(&apps.audio));
} },
_ => (),
}), }),
); );
} }
@ -336,14 +317,10 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
{ {
let apps = appstate.clone(); let apps = appstate.clone();
tray_icon.status_icon.connect_size_changed(move |_, size| { tray_icon.status_icon.connect_size_changed(move |_, size| {
try_wr!( try_wr!(apps.gui.tray_icon.update_all(&apps.prefs.borrow_mut(),
apps.gui.tray_icon.update_all( &apps.audio,
&apps.prefs.borrow_mut(), Some(size)),
&apps.audio, false);
Some(size),
),
false
);
return false; return false;
}); });
} }
@ -351,9 +328,9 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
/* tray_icon.connect_activate */ /* tray_icon.connect_activate */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
tray_icon.status_icon.connect_activate( tray_icon.status_icon.connect_activate(move |_| {
move |_| on_tray_icon_activate(&apps), on_tray_icon_activate(&apps)
); });
} }
/* tray_icon.connect_scroll_event */ /* tray_icon.connect_scroll_event */
@ -402,10 +379,9 @@ fn on_tray_icon_popup_menu(appstate: &AppS) {
} }
fn on_tray_icon_scroll_event( fn on_tray_icon_scroll_event(appstate: &AppS,
appstate: &AppS, event: &gdk::EventScroll)
event: &gdk::EventScroll, -> bool {
) -> bool {
let scroll_dir: gdk::ScrollDirection = event.get_direction(); let scroll_dir: gdk::ScrollDirection = event.get_direction();
match scroll_dir { match scroll_dir {
@ -422,10 +398,9 @@ fn on_tray_icon_scroll_event(
} }
fn on_tray_button_release_event( fn on_tray_button_release_event(appstate: &AppS,
appstate: &AppS, event_button: &gdk::EventButton)
event_button: &gdk::EventButton, -> bool {
) -> bool {
let button = event_button.get_button(); let button = event_button.get_button();
if button != 2 { if button != 2 {
@ -441,17 +416,18 @@ fn on_tray_button_release_event(
match middle_click_action { match middle_click_action {
&MiddleClickAction::ToggleMute => { &MiddleClickAction::ToggleMute => {
try_wr!(audio.toggle_mute(AudioUser::Popup), false); try_wr!(audio.toggle_mute(AudioUser::Popup), false);
}, }
&MiddleClickAction::ShowPreferences => (), &MiddleClickAction::ShowPreferences => (),
&MiddleClickAction::VolumeControl => { &MiddleClickAction::VolumeControl => {
try_wr!(execute_vol_control_command(&appstate.prefs.borrow()), false); try_wr!(execute_vol_control_command(&appstate.prefs.borrow()),
}, false);
}
&MiddleClickAction::CustomCommand => { &MiddleClickAction::CustomCommand => {
match custom_command { match custom_command {
&Some(ref cmd) => try_wr!(execute_command(cmd.as_str()), false), &Some(ref cmd) => try_wr!(execute_command(cmd.as_str()), false),
&None => warn!("No custom command found"), &None => warn!("No custom command found"),
} }
}, }
} }