This commit is contained in:
Ospald, Julian 2017-07-07 17:00:04 +02:00
parent 11b5c806d9
commit 3096347ae4
13 changed files with 604 additions and 441 deletions

View File

@ -805,12 +805,6 @@
<object class="GtkComboBoxText" id="middle_click_combo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<items>
<item id="mute" translatable="yes">Mute/Unmute</item>
<item id="prefs" translatable="yes">Show Preferences</item>
<item id="mixer" translatable="yes">Volume Control</item>
<item id="custom" translatable="yes">Custom (set below)</item>
</items>
<signal name="changed" handler="on_middle_click_combo_changed" swapped="no"/>
</object>
<packing>

View File

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

View File

@ -4,6 +4,7 @@ use gtk;
use prefs::*;
use std::cell::RefCell;
use ui_entry::Gui;
use ui_prefs_dialog::show_prefs_dialog;
// TODO: notify popups
@ -22,30 +23,43 @@ pub struct AppS {
impl AppS {
pub fn new() -> AppS {
let builder_popup_window =
gtk::Builder::new_from_string(include_str!("../data/ui/popup-window.glade"));
let builder_popup_menu = gtk::Builder::new_from_string(include_str!("../data/ui/popup-menu.glade"));
let builder_popup_window = gtk::Builder::new_from_string(include_str!(
"../data/ui/popup-window.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 gui =
Gui::new(builder_popup_window, builder_popup_menu, &prefs.borrow());
return AppS {
_cant_construct: (),
gui: gui,
audio: Audio::new(None, Some(String::from("Master")))
.unwrap(),
prefs: prefs,
};
_cant_construct: (),
gui: gui,
audio: Audio::new(None, Some(String::from("Master"))).unwrap(),
prefs: prefs,
};
}
/* some functions that need to be easily accessible */
pub fn update_tray_icon(&self) -> Result<()> {
debug!("Update tray icon!");
return self.gui.tray_icon.update_all(&self.prefs.borrow(), &self.audio, None);
return self.gui.tray_icon.update_all(
&self.prefs.borrow(),
&self.audio,
None,
);
}
pub fn update_popup_window(&self) -> Result<()> {
debug!("Update PopupWindow!");
return self.gui.popup_window.update(&self.audio);
}
}
pub fn show_preferences(&self) {
// show_prefs_dialog(self);
}
}

View File

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

View File

@ -46,8 +46,9 @@ mod ui_popup_window;
mod ui_prefs_dialog;
mod ui_tray_icon;
mod prefs;
mod support_ui;
mod support_alsa;
mod support_cmd;
mod support_ui;
use app_state::*;

View File

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

View File

@ -81,9 +81,7 @@ pub fn get_selem_names(mixer: &Mixer) -> Vec<String> {
pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
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);

View File

@ -21,18 +21,21 @@ pub fn copy_pixbuf(pixbuf: &gdk_pixbuf::Pixbuf) -> gdk_pixbuf::Pixbuf {
}
pub fn pixbuf_new_from_theme(icon_name: &str,
size: i32,
theme: &gtk::IconTheme)
-> Result<gdk_pixbuf::Pixbuf> {
pub fn pixbuf_new_from_theme(
icon_name: &str,
size: i32,
theme: &gtk::IconTheme,
) -> Result<gdk_pixbuf::Pixbuf> {
let icon_info =
theme.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty())
.ok_or(format!("Couldn't find icon {}", icon_name))?;
let icon_info = theme
.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty())
.ok_or(format!("Couldn't find icon {}", icon_name))?;
debug!("Loading stock icon {} from {:?}",
icon_name,
icon_info.get_filename().unwrap_or(PathBuf::new()));
debug!(
"Loading stock icon {} from {:?}",
icon_name,
icon_info.get_filename().unwrap_or(PathBuf::new())
);
// TODO: propagate error
let pixbuf = icon_info.load_icon().unwrap();

View File

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

View File

@ -1,46 +1,95 @@
use app_state::*;
use audio::{AudioUser, AudioSignal};
use gtk::prelude::*;
use gtk;
use std::rc::Rc;
use support_cmd::*;
use ui_prefs_dialog::*;
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
create_builder_item!(PopupMenu,
menu_window: gtk::Window,
menubar: gtk::MenuBar,
menu: gtk::Menu,
about_item: gtk::MenuItem,
prefs_item: gtk::MenuItem);
create_builder_item!(
PopupMenu,
menu_window: gtk::Window,
menubar: gtk::MenuBar,
menu: gtk::Menu,
about_item: gtk::MenuItem,
mixer_item: gtk::MenuItem,
mute_item: gtk::MenuItem,
mute_check: gtk::CheckButton,
prefs_item: gtk::MenuItem,
quit_item: gtk::MenuItem,
reload_item: gtk::MenuItem
);
pub fn init_popup_menu(appstate: Rc<AppS>) {
/* about_item.connect_activate_link */
/* audio.connect_handler */
{
let apps = appstate.clone();
let about_item = &appstate.clone()
.gui
.popup_menu
.about_item;
about_item.connect_activate(move |_| {
on_about_item_activate(&apps);
});
appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if window is hidden */
if !apps.gui.popup_menu.menu.get_visible() {
return;
}
match (s, u) {
(AudioSignal::ValuesChanged, _) => {
let muted = try_w!(apps.audio.get_mute());
apps.gui.popup_menu.mute_check.set_active(muted);
}
_ => (),
}
}));
}
/* popup_menu.menu.connect_show */
{
let apps = appstate.clone();
appstate.gui.popup_menu.menu.connect_show(move |_| {
let muted = try_w!(apps.audio.get_mute());
apps.gui.popup_menu.mute_check.set_active(muted);
});
}
/* mixer_item.connect_activate_link */
{
let apps = appstate.clone();
let mixer_item = &appstate.gui.popup_menu.mixer_item;
mixer_item.connect_activate(move |_| {
try_w!(execute_vol_control_command(&apps.prefs.borrow()));
});
}
/* about_item.connect_activate_link */
{
let prefs_item = &appstate.clone()
.gui
.popup_menu
.prefs_item;
let apps = appstate.clone();
let mute_item = &appstate.gui.popup_menu.mute_item;
mute_item.connect_activate(move |_| {
try_w!(apps.audio.toggle_mute(AudioUser::Popup));
});
}
/* about_item.connect_activate_link */
{
let apps = appstate.clone();
let about_item = &appstate.gui.popup_menu.about_item;
about_item.connect_activate(
move |_| { on_about_item_activate(&apps); },
);
}
/* about_item.connect_activate_link */
{
let apps = appstate.clone();
let prefs_item = &appstate.gui.popup_menu.prefs_item;
prefs_item.connect_activate(
move |_| { on_prefs_item_activate(appstate.clone()); },
move |_| { on_prefs_item_activate(&apps); },
);
}
}
@ -86,7 +135,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.",
}
fn on_prefs_item_activate(appstate: Rc<AppS>) {
fn on_prefs_item_activate(appstate: &Rc<AppS>) {
/* TODO: only create if needed */
show_prefs_dialog(appstate);
}

View File

@ -11,6 +11,7 @@ use gtk::prelude::*;
use gtk;
use std::cell::Cell;
use std::rc::Rc;
use support_cmd::*;
@ -20,6 +21,7 @@ pub struct PopupWindow {
pub vol_scale_adj: gtk::Adjustment,
pub vol_scale: gtk::Scale,
pub mute_check: gtk::CheckButton,
pub mixer_button: gtk::Button,
pub toggle_signal: Cell<u64>,
}
@ -31,6 +33,7 @@ impl PopupWindow {
vol_scale_adj: builder.get_object("vol_scale_adj").unwrap(),
vol_scale: builder.get_object("vol_scale").unwrap(),
mute_check: builder.get_object("mute_check").unwrap(),
mixer_button: builder.get_object("mixer_button").unwrap(),
toggle_signal: Cell::new(0),
};
}
@ -41,9 +44,6 @@ impl PopupWindow {
self.update_mute_check(&audio);
self.vol_scale.grab_focus();
grab_devices(&self.popup_window)?;
return Ok(());
}
@ -61,51 +61,72 @@ impl PopupWindow {
/* can't figure out whether channel is muted, grey out */
self.mute_check.set_active(true);
self.mute_check.set_sensitive(false);
self.mute_check.set_tooltip_text("Soundcard has no mute switch");
self.mute_check.set_tooltip_text(
"Soundcard has no mute switch",
);
}
}
glib::signal_handler_unblock(&self.mute_check, self.toggle_signal.get());
glib::signal_handler_unblock(
&self.mute_check,
self.toggle_signal.get(),
);
}
}
pub fn init_popup_window(appstate: Rc<AppS>) {
/* audio.connect_handler */
{
let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if window is hidden */
if !apps.gui.popup_window.popup_window.get_visible() {
return;
}
match (s, u) {
/* Update only mute check here
* If the user changes the volume through the popup window,
* we MUST NOT update the slider value, it's been done already.
* It means that, as long as the popup window is visible,
* the slider value reflects the value set by user,
* and not the real value reported by the audio system.
*/
(AudioSignal::ValuesChanged, AudioUser::Popup) => {
apps.gui.popup_window.update_mute_check(&apps.audio);
}
/* external change, safe to update slider too */
(AudioSignal::ValuesChanged, _) => {
try_w!(apps.gui.popup_window.update(&apps.audio));
}
_ => (),
}
}));
}
/* mute_check.connect_toggled */
{
let _appstate = appstate.clone();
let mute_check = &appstate.clone()
.gui
.popup_window
.mute_check;
let toggle_signal =
mute_check.connect_toggled(move |_| {
on_mute_check_toggled(&_appstate)
});
let mute_check = &appstate.clone().gui.popup_window.mute_check;
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 */
{
let _appstate = appstate.clone();
let popup_window = &appstate.clone()
.gui
.popup_window
.popup_window;
popup_window.connect_show(move |_| {
on_popup_window_show(&_appstate)
});
let popup_window = &appstate.clone().gui.popup_window.popup_window;
popup_window.connect_show(move |_| on_popup_window_show(&_appstate));
}
/* vol_scale_adj.connect_value_changed */
{
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(
move |_| on_vol_scale_value_changed(&_appstate),
);
@ -113,25 +134,30 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
/* popup_window.connect_event */
{
let popup_window = &appstate.clone()
.gui
.popup_window
.popup_window;
popup_window.connect_event(move |w, e| {
on_popup_window_event(w, e)
});
let popup_window = &appstate.clone().gui.popup_window.popup_window;
popup_window.connect_event(move |w, e| on_popup_window_event(w, e));
}
/* mixer_button.connect_clicked */
{
let apps = appstate.clone();
let mixer_button = &appstate.clone().gui.popup_window.mixer_button;
mixer_button.connect_clicked(move |_| {
apps.gui.popup_window.popup_window.hide();
try_w!(execute_vol_control_command(&apps.prefs.borrow()));
});
}
}
fn on_popup_window_show(appstate: &AppS) {
try_w!(appstate.gui.popup_window.update(&appstate.audio));
appstate.gui.popup_window.vol_scale.grab_focus();
try_w!(grab_devices(&appstate.gui.popup_window.popup_window));
}
fn on_popup_window_event(w: &gtk::Window,
e: &gdk::Event)
-> gtk::Inhibit {
fn on_popup_window_event(w: &gtk::Window, e: &gdk::Event) -> gtk::Inhibit {
match gdk::Event::get_event_type(e) {
gdk::EventType::GrabBroken => w.hide(),
gdk::EventType::KeyPress => {
@ -163,10 +189,7 @@ fn on_popup_window_event(w: &gtk::Window,
fn on_vol_scale_value_changed(appstate: &AppS) {
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));
}
@ -189,32 +212,40 @@ pub fn grab_devices(window: &gtk::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(());

View File

@ -45,7 +45,9 @@ pub struct PrefsDialog {
impl PrefsDialog {
pub fn new() -> PrefsDialog {
let builder = gtk::Builder::new_from_string(include_str!("../data/ui/prefs-dialog.glade"));
let builder = gtk::Builder::new_from_string(
include_str!("../data/ui/prefs-dialog.glade"),
);
let prefs_dialog = PrefsDialog {
_cant_construct: (),
prefs_dialog: builder.get_object("prefs_dialog").unwrap(),
@ -53,17 +55,21 @@ impl PrefsDialog {
card_combo: builder.get_object("card_combo").unwrap(),
chan_combo: builder.get_object("chan_combo").unwrap(),
vol_meter_draw_check: builder.get_object("vol_meter_draw_check")
vol_meter_draw_check: builder
.get_object("vol_meter_draw_check")
.unwrap(),
vol_meter_pos_spin: builder.get_object("vol_meter_pos_spin")
vol_meter_pos_spin: builder
.get_object("vol_meter_pos_spin")
.unwrap(),
vol_meter_color_button: builder.get_object("vol_meter_color_button")
vol_meter_color_button: builder
.get_object("vol_meter_color_button")
.unwrap(),
system_theme: builder.get_object("system_theme").unwrap(),
vol_control_entry: builder.get_object("vol_control_entry").unwrap(),
scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(),
middle_click_combo: builder.get_object("middle_click_combo")
middle_click_combo: builder
.get_object("middle_click_combo")
.unwrap(),
custom_entry: builder.get_object("custom_entry").unwrap(),
@ -83,17 +89,25 @@ impl PrefsDialog {
pub fn from_prefs(&self, prefs: &Prefs) {
/* DevicePrefs */
self.card_combo.remove_all();
self.card_combo.append_text(prefs.device_prefs.card.as_str());
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.append_text(prefs.device_prefs.channel.as_str());
self.chan_combo.append_text(
prefs.device_prefs.channel.as_str(),
);
self.chan_combo.set_active(0);
/* ViewPrefs */
self.vol_meter_draw_check.set_active(prefs.view_prefs.draw_vol_meter);
self.vol_meter_pos_spin.set_value(prefs.view_prefs.vol_meter_offset as
f64);
self.vol_meter_draw_check.set_active(
prefs.view_prefs.draw_vol_meter,
);
self.vol_meter_pos_spin.set_value(
prefs.view_prefs.vol_meter_offset as
f64,
);
// TODO don't convert like that
let rgba = gdk::RGBA {
@ -106,51 +120,71 @@ impl PrefsDialog {
self.system_theme.set_active(prefs.view_prefs.system_theme);
/* BehaviorPrefs */
self.vol_control_entry.set_text(prefs.behavior_prefs
.vol_control_cmd
.as_ref()
.unwrap_or(&String::from(""))
.as_str());
self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step);
self.vol_control_entry.set_text(
prefs
.behavior_prefs
.vol_control_cmd
.as_ref()
.unwrap_or(&String::from(""))
.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
// also check to_prefs()
self.middle_click_combo.append_text("Toggle Mute");
self.middle_click_combo.append_text("Show Preferences");
self.middle_click_combo.append_text("Volume Control");
self.middle_click_combo.append_text("Custom Command");
self.middle_click_combo.set_active(prefs.behavior_prefs
.middle_click_action
.into());
self.custom_entry.set_text(prefs.behavior_prefs
.custom_command
.as_ref()
.unwrap_or(&String::from(""))
.as_str());
self.middle_click_combo.append_text("Custom Command (set below)");
self.middle_click_combo.set_active(
prefs
.behavior_prefs
.middle_click_action
.into(),
);
self.custom_entry.set_text(
prefs
.behavior_prefs
.custom_command
.as_ref()
.unwrap_or(&String::from(""))
.as_str(),
);
/* NotifyPrefs */
self.noti_enable_check
.set_active(prefs.notify_prefs.enable_notifications);
self.noti_timeout_spin
.set_value(prefs.notify_prefs.notifcation_timeout as f64);
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);
self.noti_enable_check.set_active(
prefs
.notify_prefs
.enable_notifications,
);
self.noti_timeout_spin.set_value(
prefs.notify_prefs.notifcation_timeout as
f64,
);
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 {
// TODO: remove duplication with default instance
let device_prefs =
DevicePrefs {
card: self.card_combo
.get_active_text()
.unwrap_or(String::from("(default)")),
channel: self.chan_combo
.get_active_text()
.unwrap_or(String::from("Master")),
};
let device_prefs = DevicePrefs {
card: self.card_combo.get_active_text().unwrap_or(
String::from("(default)"),
),
channel: self.chan_combo.get_active_text().unwrap_or(
String::from("Master"),
),
};
// TODO don't convert like that
let vol_meter_color = VolColor {
@ -166,19 +200,16 @@ impl PrefsDialog {
vol_meter_color,
};
let vol_control_cmd =
self.vol_control_entry.get_text().and_then(|x| if x.is_empty() {
None
} else {
Some(x)
});
let vol_control_cmd = self.vol_control_entry.get_text().and_then(|x| {
if x.is_empty() { None } else { Some(x) }
});
let custom_command =
self.custom_entry.get_text().and_then(|x| if x.is_empty() {
None
} else {
Some(x)
});
None
} else {
Some(x)
});
let behavior_prefs = BehaviorPrefs {
vol_control_cmd,
@ -190,28 +221,25 @@ impl PrefsDialog {
let notify_prefs = NotifyPrefs {
enable_notifications: self.noti_enable_check.get_active(),
notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as
i64,
i64,
notify_mouse_scroll: self.noti_mouse_check.get_active(),
notify_popup: self.noti_popup_check.get_active(),
notify_external: self.noti_ext_check.get_active(),
};
return Prefs {
device_prefs,
view_prefs,
behavior_prefs,
notify_prefs,
};
device_prefs,
view_prefs,
behavior_prefs,
notify_prefs,
};
}
}
pub fn show_prefs_dialog(appstate: Rc<AppS>) {
if appstate.gui
.prefs_dialog
.borrow()
.is_some() {
pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
if appstate.gui.prefs_dialog.borrow().is_some() {
return;
}
@ -225,7 +253,6 @@ pub fn show_prefs_dialog(appstate: Rc<AppS>) {
prefs_dialog.from_prefs(&appstate.prefs.borrow());
prefs_dialog_w.set_transient_for(&appstate.gui.popup_menu.menu_window);
// TODO: destruct PrefsDialog when clicking Ok/Apply
prefs_dialog_w.present();
}
}
@ -239,7 +266,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
let apps = appstate.clone();
let m_pd = appstate.gui.prefs_dialog.borrow();
let pd = m_pd.as_ref().unwrap();
pd.prefs_dialog.connect_show(move |_| { on_prefs_dialog_show(&apps); });
pd.prefs_dialog.connect_show(
move |_| { on_prefs_dialog_show(&apps); },
);
}
/* prefs_dialog.connect_show */
@ -257,6 +286,7 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>) {
}
// TODO: does Rc<AppS> get dropped?!
if response_id != ResponseType::Apply.into() {
let mut prefs_dialog = apps.gui.prefs_dialog.borrow_mut();
prefs_dialog.as_ref().unwrap().prefs_dialog.destroy();
@ -307,8 +337,8 @@ fn on_prefs_dialog_show(appstate: &AppS) {
/* set card combo */
let cur_card_name = try_w!(acard.card_name(),
"Can't get current card name!");
let cur_card_name =
try_w!(acard.card_name(), "Can't get current card name!");
let available_card_names = get_alsa_card_names();
/* set_active_id doesn't work, so save the index */
@ -351,8 +381,9 @@ fn on_card_combo_changed(appstate: &AppS) {
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_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();
@ -360,9 +391,11 @@ fn on_card_combo_changed(appstate: &AppS) {
};
if active_card_item != cur_card_name {
appstate.audio.switch_acard(Some(cur_card_name),
active_chan_item,
AudioUser::PrefsWindow);
appstate.audio.switch_acard(
Some(cur_card_name),
active_chan_item,
AudioUser::PrefsWindow,
);
}
}
@ -372,8 +405,9 @@ fn on_chan_combo_changed(appstate: &AppS) {
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 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()
@ -384,10 +418,10 @@ fn on_chan_combo_changed(appstate: &AppS) {
};
if active_chan_item != cur_chan_name {
appstate.audio.switch_acard(cur_card_name,
Some(active_chan_item),
AudioUser::PrefsWindow);
appstate.audio.switch_acard(
cur_card_name,
Some(active_chan_item),
AudioUser::PrefsWindow,
);
}
}

View File

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