This commit is contained in:
Julian Ospald 2017-07-05 20:27:16 +02:00
parent 0847a1087b
commit c010eb6ba9
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
13 changed files with 477 additions and 404 deletions

View File

@ -21,6 +21,7 @@ error-chain = { path = "3rdparty/error-chain" }
toml = "^0.4.2" toml = "^0.4.2"
serde_derive = "^1.0.9" serde_derive = "^1.0.9"
serde = "^1.0.9" serde = "^1.0.9"
xdg = "*"
[dependencies.gtk] [dependencies.gtk]
git = "https://github.com/gtk-rs/gtk.git" git = "https://github.com/gtk-rs/gtk.git"

View File

@ -36,11 +36,10 @@ 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) => get_alsa_card_by_name(name)?,
@ -51,10 +50,10 @@ 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 {
@ -69,8 +68,8 @@ impl AlsaCard {
/* 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 +82,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 +113,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 +140,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 +199,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,14 +221,13 @@ 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 {
@ -250,4 +246,3 @@ extern "C" fn watch_cb(
return true as glib_sys::gboolean; return true as glib_sys::gboolean;
} }

View File

@ -15,16 +15,14 @@ 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!("../data/ui/popup-window.glade"));
)); let builder_popup_menu = gtk::Builder::new_from_string(include_str!("../data/ui/popup-menu.glade"));
let builder_popup_menu = gtk::Builder::new_from_string(
include_str!("../data/ui/popup-menu.glade"),
);
return AppS { return AppS {
gui: Gui::new(builder_popup_window, builder_popup_menu), gui: Gui::new(builder_popup_window, builder_popup_menu),
audio: Audio::new(None, Some(String::from("Master"))).unwrap(), audio: Audio::new(None, Some(String::from("Master")))
.unwrap(),
}; };
} }
} }
@ -39,10 +37,9 @@ 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, -> Gui {
) -> Gui {
return Gui { return Gui {
tray_icon: TrayIcon::new().unwrap(), tray_icon: TrayIcon::new().unwrap(),
popup_window: PopupWindow::new(builder_popup_window), popup_window: PopupWindow::new(builder_popup_window),
@ -52,20 +49,16 @@ impl Gui {
} }
create_builder_item!( create_builder_item!(PopupWindow,
PopupWindow,
popup_window: gtk::Window, popup_window: gtk::Window,
vol_scale_adj: gtk::Adjustment, vol_scale_adj: gtk::Adjustment,
vol_scale: gtk::Scale, vol_scale: gtk::Scale,
mute_check: gtk::CheckButton mute_check: gtk::CheckButton);
);
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,
prefs_item: gtk::MenuItem prefs_item: gtk::MenuItem);
);

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,7 +84,9 @@ 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)
}) })
}; };
@ -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()
.unwrap(),
self.acard
.borrow()
.chan_name()
.unwrap(),
new_vol, new_vol,
user user);
); self.acard
self.acard.borrow().set_vol(new_vol)?; .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()
.unwrap(),
self.acard
.borrow()
.chan_name()
.unwrap(),
(new_vol - old_vol), (new_vol - old_vol),
new_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()
.unwrap(),
self.acard
.borrow()
.chan_name()
.unwrap(),
(new_vol - old_vol), (new_vol - old_vol),
new_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.borrow().card_name().unwrap(), self.acard
self.acard.borrow().chan_name().unwrap(), .borrow()
user .card_name()
); .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 {:?}",
debug!(
"Invoking handlers for signal {:?} by user {:?}",
signal, signal,
user 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

@ -2,12 +2,15 @@ use alsa;
use glib; use glib;
use std::convert::From; use std::convert::From;
use std; use std;
use toml;
error_chain! { error_chain! {
foreign_links { foreign_links {
Alsa(alsa::Error); Alsa(alsa::Error);
IO(std::io::Error);
Toml(toml::de::Error);
} }
} }

View File

@ -26,6 +26,7 @@ extern crate gobject_sys;
extern crate gtk; extern crate gtk;
extern crate gtk_sys; extern crate gtk_sys;
extern crate libc; extern crate libc;
extern crate xdg;
use std::rc::Rc; use std::rc::Rc;
@ -63,10 +64,11 @@ fn main() {
let apps = Rc::new(AppS::new()); let apps = Rc::new(AppS::new());
let mut prefs = prefs::Prefs::new_from_def(); let mut prefs = prefs::Prefs::new().unwrap();
prefs.behavior_prefs.middle_click_action = MiddleClickAction::CustomCommand(String::from("Gaga"));
println!("Channel: {:?}", prefs.to_str()); println!("Channel: {:?}", prefs.to_str());
prefs.store_config();
// println!("Channel: {:?}", prefs.to_str());
ui_entry::init(apps); ui_entry::init(apps);

View File

@ -1,52 +1,35 @@
use errors::*; use errors::*;
use std::path::Path;
use glib;
use toml; use toml;
use xdg;
use std::fs::File;
use std::io::prelude::*;
const DEFAULT_PREFS: &str = "[device_prefs]
card = \"default\"
channel = \"Master\"
[view_prefs] const VOL_CONTROL_COMMANDS: [&str; 3] =
draw_vol_meter = true ["gnome-alsamixer", "xfce4-mixer", "alsamixergui"];
vol_meter_offset = 10
vol_meter_color = { red = 245, blue = 121, green = 0 }
system_theme = true
[behavior_prefs]
vol_control_cmd = \"\"
vol_scroll_step = 5.0
middle_click_action = \"ToggleMute\"
[notify_prefs]
enable_notifications = true
notifcation_timeout = 1500
notify_mouse_scroll = true
notify_popup = true
notify_external = true";
const VOL_CONTROL_COMMANDS: [&str; 3] = [
"gnome-alsamixer",
"xfce4-mixer",
"alsamixergui"
];
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(tag = "action", rename_all = "snake_case")]
pub enum MiddleClickAction { pub enum MiddleClickAction {
ToggleMute, ToggleMute,
ShowPreferences, ShowPreferences,
VolumeControl, VolumeControl,
CustomCommand(String), CustomCommand { cmd: String },
}
impl Default for MiddleClickAction {
fn default() -> MiddleClickAction {
return MiddleClickAction::ToggleMute;
}
} }
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct Prefs { pub struct Prefs {
pub device_prefs: DevicePrefs, pub device_prefs: DevicePrefs,
pub view_prefs: ViewPrefs, pub view_prefs: ViewPrefs,
@ -55,38 +38,99 @@ pub struct Prefs {
// TODO: HotKeys? // TODO: HotKeys?
} }
impl Default for Prefs {
fn default() -> Prefs {
return Prefs {
device_prefs: DevicePrefs::default(),
view_prefs: ViewPrefs::default(),
behavior_prefs: BehaviorPrefs::default(),
notify_prefs: NotifyPrefs::default(),
};
}
}
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[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 {
fn default() -> DevicePrefs {
return DevicePrefs {
card: String::from("(default)"),
channel: String::from("Master"),
};
}
}
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct ViewPrefs { pub struct ViewPrefs {
pub draw_vol_meter: bool, pub draw_vol_meter: bool,
pub vol_meter_offset: i64, pub vol_meter_offset: i64,
pub vol_meter_color: VolColor,
pub system_theme: bool, pub system_theme: bool,
pub vol_meter_color: VolColor,
// TODO: Display text folume/text volume pos? // 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(),
};
}
}
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct VolColor { pub struct VolColor {
pub red: u8, pub red: u8,
pub green: u8, pub green: u8,
pub blue: u8, pub blue: u8,
} }
impl Default for VolColor {
fn default() -> VolColor {
return VolColor {
red: 245,
green: 180,
blue: 0,
};
}
}
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct BehaviorPrefs { pub struct BehaviorPrefs {
pub vol_control_cmd: String, vol_control_cmd: Option<String>,
pub vol_scroll_step: f64, pub vol_scroll_step: f64,
pub middle_click_action: MiddleClickAction, pub middle_click_action: MiddleClickAction,
// TODO: fine scroll step? // TODO: fine scroll step?
} }
impl Default for BehaviorPrefs {
fn default() -> BehaviorPrefs {
return BehaviorPrefs {
vol_control_cmd: None,
vol_scroll_step: 5.0,
middle_click_action: MiddleClickAction::default(),
};
}
}
#[derive(Deserialize, Debug, Serialize)] #[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct NotifyPrefs { pub struct NotifyPrefs {
pub enable_notifications: bool, pub enable_notifications: bool,
pub notifcation_timeout: i64, pub notifcation_timeout: i64,
@ -96,44 +140,84 @@ pub struct NotifyPrefs {
// TODO: notify_hotkeys? // 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,
};
}
}
impl Prefs { impl Prefs {
// pub fn set_blah(&mut self) { pub fn new() -> Result<Prefs> {
// self.vol_scroll_step = 5.0; let m_config_file = get_xdg_dirs().find_config_file("pnmixer.toml");
// } match m_config_file {
Some(c) => {
debug!("Config file present at {:?}, using it.", c);
// pub fn new() -> Prefs { let mut f = File::open(c)?;
// // load from config let mut buffer = vec![];
f.read_to_end(&mut buffer)?;
// } let prefs = toml::from_slice(buffer.as_slice())?;
// pub fn reload_from_config(&self) { return Ok(prefs);
}
None => {
debug!("No config file present, creating one with defaults.");
// } let prefs = Prefs::default();
prefs.store_config()?;
return Ok(prefs);
// pub fn save_to_config() -> Result<()> { }
}
// }
// fn config_path() -> String {
// }
fn ensure_config_path() {
} }
pub fn new_from_def() -> Prefs {
let prefs: Prefs = toml::from_str(DEFAULT_PREFS).unwrap(); pub fn reload_config(&mut self) -> Result<()> {
return prefs; debug!("Reloading config...");
let new_prefs = Prefs::new()?;
*self = new_prefs;
return Ok(());
}
pub fn store_config(&self) -> Result<()> {
let config_path = get_xdg_dirs().place_config_file("pnmixer.toml")
.from_err()?;
debug!("Storing config in {:?}", config_path);
let mut f = File::create(config_path)?;
f.write_all(self.to_str().as_bytes())?;
return Ok(());
} }
pub fn to_str(&self) -> String { pub fn to_str(&self) -> String {
return toml::to_string(self).unwrap(); return toml::to_string(self).unwrap();
} }
// TODO: implement
pub fn vol_control_cmd() -> String {
return String::from("");
}
} }
fn get_xdg_dirs() -> xdg::BaseDirectories {
return xdg::BaseDirectories::with_prefix("pnmixer-rs").unwrap();
}

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);
@ -103,4 +105,3 @@ pub fn percent_to_vol(vol: f64, range: (i64, i64)) -> i64 {
/* TODO: precision? Use direction. */ /* TODO: precision? Use direction. */
return _v as i64; return _v as i64;
} }

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();
@ -59,5 +56,3 @@ pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> {
bail!("Uh-oh"); bail!("Uh-oh");
} }
} }

View File

@ -14,15 +14,21 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
/* about_item.connect_activate_link */ /* about_item.connect_activate_link */
{ {
let apps = appstate.clone(); let apps = appstate.clone();
let about_item = &appstate.clone().gui.popup_menu.about_item; let about_item = &appstate.clone()
about_item.connect_activate( .gui
move |_| { on_about_item_activate(&apps); }, .popup_menu
); .about_item;
about_item.connect_activate(move |_| {
on_about_item_activate(&apps);
});
} }
/* about_item.connect_activate_link */ /* about_item.connect_activate_link */
{ {
let prefs_item = &appstate.clone().gui.popup_menu.prefs_item; let prefs_item = &appstate.clone()
.gui
.popup_menu
.prefs_item;
prefs_item.connect_activate( prefs_item.connect_activate(
move |_| { on_prefs_item_activate(appstate.clone()); }, move |_| { on_prefs_item_activate(appstate.clone()); },
); );

View File

@ -18,25 +18,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()
toggle_signal = mute_check.connect_toggled( .gui
move |_| on_mute_check_toggled(&_appstate), .popup_window
); .mute_check;
toggle_signal =
mute_check.connect_toggled(move |_| {
on_mute_check_toggled(&_appstate)
});
} }
/* 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()
popup_window.connect_show( .gui
move |w| on_popup_window_show(w, &_appstate, toggle_signal), .popup_window
); .popup_window;
popup_window.connect_show(move |w| {
on_popup_window_show(w,
&_appstate,
toggle_signal)
});
} }
/* 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),
); );
@ -45,7 +57,10 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
/* popup_window.connect_event */ /* popup_window.connect_event */
{ {
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_event(move |w, e| { popup_window.connect_event(move |w, e| {
on_popup_window_event(w, e, &_appstate) on_popup_window_event(w, e, &_appstate)
}); });
@ -53,11 +68,9 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
} }
fn on_popup_window_show( fn on_popup_window_show(window: &gtk::Window,
window: &gtk::Window,
appstate: &AppS, appstate: &AppS,
toggle_signal: u64, toggle_signal: u64) {
) {
let audio = &appstate.audio; let audio = &appstate.audio;
let popup_window = &appstate.gui.popup_window; let popup_window = &appstate.gui.popup_window;
@ -72,11 +85,10 @@ fn on_popup_window_show(
} }
fn on_popup_window_event( fn on_popup_window_event(w: &gtk::Window,
w: &gtk::Window,
e: &gdk::Event, e: &gdk::Event,
appstate: &AppS, appstate: &AppS)
) -> gtk::Inhibit { -> gtk::Inhibit {
match gdk::Event::get_event_type(e) { match gdk::Event::get_event_type(e) {
gdk::EventType::GrabBroken => w.hide(), gdk::EventType::GrabBroken => w.hide(),
gdk::EventType::KeyPress => { gdk::EventType::KeyPress => {
@ -108,7 +120,10 @@ fn on_popup_window_event(
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));
} }
@ -120,11 +135,9 @@ fn on_mute_check_toggled(appstate: &AppS) {
} }
pub fn update_mute_check( pub fn update_mute_check(appstate: &AppS,
appstate: &AppS,
toggle_signal: u64, toggle_signal: u64,
muted: Result<bool>, muted: Result<bool>) {
) {
let check_button = &appstate.gui.popup_window.mute_check; let check_button = &appstate.gui.popup_window.mute_check;
glib::signal_handler_block(check_button, toggle_signal); glib::signal_handler_block(check_button, toggle_signal);
@ -157,40 +170,32 @@ 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

@ -7,18 +7,14 @@ use support_alsa::*;
create_builder_item!( create_builder_item!(PrefsDialog,
PrefsDialog,
prefs_dialog: gtk::Dialog, prefs_dialog: gtk::Dialog,
card_combo: gtk::ComboBoxText, card_combo: gtk::ComboBoxText,
chan_combo: gtk::ComboBoxText chan_combo: gtk::ComboBoxText);
);
pub fn show_prefs_dialog(appstate: Rc<AppS>) { pub fn show_prefs_dialog(appstate: Rc<AppS>) {
let builder_prefs_dialog = gtk::Builder::new_from_string( let builder_prefs_dialog = gtk::Builder::new_from_string(include_str!("../data/ui/prefs-dialog.glade"));
include_str!("../data/ui/prefs-dialog.glade"),
);
let prefs_dialog = Rc::new(PrefsDialog::new(builder_prefs_dialog)); let prefs_dialog = Rc::new(PrefsDialog::new(builder_prefs_dialog));
init_prefs_dialog(&appstate, &prefs_dialog); init_prefs_dialog(&appstate, &prefs_dialog);
{ {
@ -51,9 +47,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>, prefs_dialog: &Rc<PrefsDialog>) {
let pd = prefs_dialog.clone(); let pd = prefs_dialog.clone();
// TODO: refill channel combo // TODO: refill channel combo
card_combo.connect_changed( card_combo.connect_changed(move |_| {
move |_| { on_card_combo_changed(&apps, &pd); }, on_card_combo_changed(&apps, &pd);
); });
} }
/* card_combo.connect_changed */ /* card_combo.connect_changed */
{ {
@ -61,9 +57,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>, prefs_dialog: &Rc<PrefsDialog>) {
let chan_combo = &prefs_dialog.chan_combo; let chan_combo = &prefs_dialog.chan_combo;
let pd = prefs_dialog.clone(); let pd = prefs_dialog.clone();
chan_combo.connect_changed( chan_combo.connect_changed(move |_| {
move |_| { on_chan_combo_changed(&apps, &pd); }, on_chan_combo_changed(&apps, &pd);
); });
} }
} }
@ -75,8 +71,8 @@ fn on_prefs_dialog_show(appstate: &AppS, prefs_dialog: &PrefsDialog) {
/* 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 */
@ -117,9 +113,8 @@ fn on_prefs_dialog_show(appstate: &AppS, prefs_dialog: &PrefsDialog) {
fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) { fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
let card_combo = &prefs_dialog.card_combo; let card_combo = &prefs_dialog.card_combo;
let chan_combo = &prefs_dialog.chan_combo; let chan_combo = &prefs_dialog.chan_combo;
let active_card_item = try_w!(card_combo.get_active_text().ok_or( let active_card_item =
"No active Card item found", try_w!(card_combo.get_active_text().ok_or("No active Card item found"));
));
let active_chan_item = chan_combo.get_active_id(); let active_chan_item = chan_combo.get_active_id();
let cur_card_name = { let cur_card_name = {
let acard = appstate.audio.acard.borrow(); let acard = appstate.audio.acard.borrow();
@ -127,11 +122,9 @@ fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
}; };
if active_card_item != cur_card_name { if active_card_item != cur_card_name {
appstate.audio.switch_acard( appstate.audio.switch_acard(Some(cur_card_name),
Some(cur_card_name),
active_chan_item, active_chan_item,
AudioUser::PrefsWindow AudioUser::PrefsWindow);
);
} }
} }
@ -139,9 +132,8 @@ fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) { fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
let card_combo = &prefs_dialog.card_combo; let card_combo = &prefs_dialog.card_combo;
let chan_combo = &prefs_dialog.chan_combo; let chan_combo = &prefs_dialog.chan_combo;
let active_chan_item = try_w!(chan_combo.get_active_text().ok_or( let active_chan_item =
"No active Chan item found", try_w!(chan_combo.get_active_text().ok_or("No active Chan item found"));
));
let cur_card_name = { let cur_card_name = {
let acard = appstate.audio.acard.borrow(); let acard = appstate.audio.acard.borrow();
acard.card_name().ok() acard.card_name().ok()
@ -152,10 +144,8 @@ fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
}; };
if active_chan_item != cur_chan_name { if active_chan_item != cur_chan_name {
appstate.audio.switch_acard( appstate.audio.switch_acard(cur_card_name,
cur_card_name,
Some(active_chan_item), Some(active_chan_item),
AudioUser::PrefsWindow AudioUser::PrefsWindow);
);
} }
} }

View File

@ -34,7 +34,12 @@ impl TrayIcon {
let audio_pix = AudioPix::new_from_pnmixer()?; let audio_pix = AudioPix::new_from_pnmixer()?;
let status_icon = gtk::StatusIcon::new(); let status_icon = gtk::StatusIcon::new();
return Ok(TrayIcon { volmeter, audio_pix, status_icon, icon_size: Cell::new(ICON_MIN_SIZE) }); return Ok(TrayIcon {
volmeter,
audio_pix,
status_icon,
icon_size: Cell::new(ICON_MIN_SIZE),
});
} }
fn update(&self, audio: &Audio, m_size: Option<i64>) { fn update(&self, audio: &Audio, m_size: Option<i64>) {
@ -45,14 +50,13 @@ impl TrayIcon {
} else { } else {
self.icon_size.set(s); self.icon_size.set(s);
} }
}, }
None => (), None => (),
} }
let cur_vol = try_w!(audio.vol()); let cur_vol = try_w!(audio.vol());
let pixbuf = self.audio_pix.select_pix(audio.vol_level()); let pixbuf = self.audio_pix.select_pix(audio.vol_level());
let vol_pix = try_w!(self.volmeter.meter_draw(cur_vol as i64, let vol_pix = try_w!(self.volmeter.meter_draw(cur_vol as i64, &pixbuf));
&pixbuf));
self.status_icon.set_from_pixbuf(Some(&vol_pix)); self.status_icon.set_from_pixbuf(Some(&vol_pix));
} }
@ -88,25 +92,18 @@ impl VolMeter {
} }
// 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;
@ -115,21 +112,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 >= 0 && (x + vm_width) <= i_width,
"x coordinate invalid: {}", "x coordinate invalid: {}",
x 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.
@ -165,9 +157,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());
);
} }
} }
@ -215,9 +206,7 @@ impl AudioPix {
} }
pub fn new_from_pnmixer() -> Result<AudioPix> { pub fn new_from_pnmixer() -> Result<AudioPix> {
gtk::IconTheme::get_default().ok_or( gtk::IconTheme::get_default().ok_or("Couldn't get default icon theme")?;
"Couldn't get default icon theme",
)?;
let pix = AudioPix { let pix = AudioPix {
muted: pixbuf_new_from_file("pnmixer-muted.png")?, muted: pixbuf_new_from_file("pnmixer-muted.png")?,
low: pixbuf_new_from_file("pnmixer-low.png")?, low: pixbuf_new_from_file("pnmixer-low.png")?,
@ -273,7 +262,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(move |_| on_tray_icon_activate(&apps)); tray_icon.status_icon.connect_activate(move |_| {
on_tray_icon_activate(&apps)
});
} }
/* tray_icon.connect_scroll_event */ /* tray_icon.connect_scroll_event */
@ -322,10 +313,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 audio = &appstate.audio; let audio = &appstate.audio;
@ -344,10 +334,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 {