Update
This commit is contained in:
parent
0c881e0c8b
commit
17a6ba615c
@ -40,7 +40,7 @@ impl Gui {
|
|||||||
builder_popup_menu: gtk::Builder)
|
builder_popup_menu: gtk::Builder)
|
||||||
-> Gui {
|
-> Gui {
|
||||||
return Gui {
|
return Gui {
|
||||||
status_icon: gtk::StatusIcon::new_from_icon_name("pnmixer"),
|
status_icon: gtk::StatusIcon::new(),
|
||||||
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),
|
||||||
};
|
};
|
||||||
|
89
src/audio.rs
89
src/audio.rs
@ -9,23 +9,33 @@ use alsa_pn::*;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum VolLevel {
|
||||||
|
Muted,
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Off,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum AudioUser {
|
pub enum AudioUser {
|
||||||
AudioUserUnknown,
|
Unknown,
|
||||||
AudioUserPopup,
|
Popup,
|
||||||
AudioUserTrayIcon,
|
TrayIcon,
|
||||||
AudioUserHotkeys,
|
Hotkeys,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum AudioSignal {
|
pub enum AudioSignal {
|
||||||
AudioNoCard,
|
NoCard,
|
||||||
AudioCardInitialized,
|
CardInitialized,
|
||||||
AudioCardCleanedUp,
|
CardCleanedUp,
|
||||||
AudioCardDisconnected,
|
CardDisconnected,
|
||||||
AudioCardError,
|
CardError,
|
||||||
AudioValuesChanged,
|
ValuesChanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -125,6 +135,7 @@ impl Audio {
|
|||||||
.chan_name()
|
.chan_name()
|
||||||
.unwrap());
|
.unwrap());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
// TODO: invoke handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -133,12 +144,27 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn vol_level(&self) -> VolLevel {
|
||||||
|
let muted = self.get_mute().unwrap_or(false);
|
||||||
|
if muted {
|
||||||
|
return VolLevel::Muted;
|
||||||
|
}
|
||||||
|
let cur_vol = try_r!(self.vol(), VolLevel::Muted);
|
||||||
|
match cur_vol {
|
||||||
|
0. => return VolLevel::Off,
|
||||||
|
0.0...33.0 => return VolLevel::Low,
|
||||||
|
0.0...66.0 => return VolLevel::Medium,
|
||||||
|
0.0...100.0 => return VolLevel::High,
|
||||||
|
_ => return VolLevel::Off,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn set_vol(&self, new_vol: f64, user: AudioUser) -> Result<()> {
|
pub fn set_vol(&self, new_vol: f64, user: AudioUser) -> Result<()> {
|
||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
// TODO invoke handlers, make use of user
|
|
||||||
|
|
||||||
debug!("Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
|
debug!("Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
|
||||||
self.acard
|
self.acard
|
||||||
@ -151,7 +177,14 @@ impl Audio {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
new_vol,
|
new_vol,
|
||||||
user);
|
user);
|
||||||
return self.acard.borrow().set_vol(new_vol);
|
self.acard
|
||||||
|
.borrow()
|
||||||
|
.set_vol(new_vol)?;
|
||||||
|
|
||||||
|
invoke_handlers(&self.handlers.borrow(),
|
||||||
|
AudioSignal::ValuesChanged,
|
||||||
|
user);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -175,7 +208,12 @@ impl Audio {
|
|||||||
(new_vol - old_vol),
|
(new_vol - old_vol),
|
||||||
new_vol);
|
new_vol);
|
||||||
|
|
||||||
return self.set_vol(new_vol, user);
|
self.set_vol(new_vol, user)?;
|
||||||
|
|
||||||
|
invoke_handlers(&self.handlers.borrow(),
|
||||||
|
AudioSignal::ValuesChanged,
|
||||||
|
user);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -199,7 +237,12 @@ impl Audio {
|
|||||||
(new_vol - old_vol),
|
(new_vol - old_vol),
|
||||||
new_vol);
|
new_vol);
|
||||||
|
|
||||||
return self.set_vol(new_vol, user);
|
self.set_vol(new_vol, user)?;
|
||||||
|
|
||||||
|
invoke_handlers(&self.handlers.borrow(),
|
||||||
|
AudioSignal::ValuesChanged,
|
||||||
|
user);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -216,7 +259,8 @@ impl Audio {
|
|||||||
pub fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
|
pub fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
|
||||||
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();
|
||||||
// TODO invoke handlers, make use of user
|
|
||||||
|
|
||||||
|
|
||||||
debug!("Setting mute to {} on card {:?} and chan {:?} by user {:?}",
|
debug!("Setting mute to {} on card {:?} and chan {:?} by user {:?}",
|
||||||
mute,
|
mute,
|
||||||
@ -230,7 +274,14 @@ impl Audio {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
user);
|
user);
|
||||||
|
|
||||||
return self.acard.borrow().set_mute(mute);
|
self.acard
|
||||||
|
.borrow()
|
||||||
|
.set_mute(mute)?;
|
||||||
|
|
||||||
|
invoke_handlers(&self.handlers.borrow(),
|
||||||
|
AudioSignal::ValuesChanged,
|
||||||
|
user);
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -258,8 +309,6 @@ fn on_alsa_event(last_action_timestamp: &mut i64,
|
|||||||
alsa_event: AlsaEvent) {
|
alsa_event: AlsaEvent) {
|
||||||
let last: i64 = *last_action_timestamp;
|
let last: i64 = *last_action_timestamp;
|
||||||
|
|
||||||
debug!("Last: {}", last);
|
|
||||||
|
|
||||||
if last != 0 {
|
if last != 0 {
|
||||||
let now: i64 = glib::get_monotonic_time();
|
let now: i64 = glib::get_monotonic_time();
|
||||||
let delay: i64 = now - last;
|
let delay: i64 = now - last;
|
||||||
@ -278,8 +327,8 @@ fn on_alsa_event(last_action_timestamp: &mut i64,
|
|||||||
AlsaEvent::AlsaCardValuesChanged => {
|
AlsaEvent::AlsaCardValuesChanged => {
|
||||||
debug!("AlsaCardValuesChanged");
|
debug!("AlsaCardValuesChanged");
|
||||||
invoke_handlers(handlers,
|
invoke_handlers(handlers,
|
||||||
self::AudioSignal::AudioValuesChanged,
|
self::AudioSignal::ValuesChanged,
|
||||||
self::AudioUser::AudioUserUnknown);
|
self::AudioUser::Unknown);
|
||||||
}
|
}
|
||||||
e => warn!("Unhandled alsa event: {:?}", e),
|
e => warn!("Unhandled alsa event: {:?}", e),
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use alsa;
|
use alsa;
|
||||||
|
use glib;
|
||||||
use std::convert::From;
|
use std::convert::From;
|
||||||
use std;
|
use std;
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use app_state::*;
|
use app_state::*;
|
||||||
use audio::{AudioSignal, AudioUser};
|
use audio::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use ui_popup_menu::*;
|
use ui_popup_menu::*;
|
||||||
use ui_popup_window::*;
|
use ui_popup_window::*;
|
||||||
@ -11,16 +11,14 @@ use ui_prefs_dialog::*;
|
|||||||
pub fn init(appstate: Rc<AppS>) {
|
pub fn init(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| match (s, u) {
|
||||||
match (s, u) {
|
(AudioSignal::ValuesChanged,
|
||||||
(AudioSignal::AudioValuesChanged,
|
AudioUser::Unknown) => {
|
||||||
AudioUser::AudioUserUnknown) => {
|
|
||||||
debug!("External volume change!");
|
debug!("External volume change!");
|
||||||
|
|
||||||
}
|
}
|
||||||
_ => debug!("Nix"),
|
_ => debug!("Nix"),
|
||||||
}
|
}));
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init_tray_icon(appstate.clone());
|
init_tray_icon(appstate.clone());
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use app_state::*;
|
use app_state::*;
|
||||||
use audio::AudioUser::*;
|
use audio::*;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use gdk::DeviceExt;
|
use gdk::DeviceExt;
|
||||||
use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK};
|
use gdk::{GrabOwnership, GrabStatus, BUTTON_PRESS_MASK, KEY_PRESS_MASK};
|
||||||
@ -125,7 +125,7 @@ fn on_vol_scale_value_changed(appstate: &AppS) {
|
|||||||
.vol_scale
|
.vol_scale
|
||||||
.get_value();
|
.get_value();
|
||||||
|
|
||||||
try_w!(audio.set_vol(val, AudioUserPopup));
|
try_w!(audio.set_vol(val, AudioUser::Popup));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -133,7 +133,7 @@ fn on_mute_check_toggled(appstate: &AppS) {
|
|||||||
let audio = &appstate.audio;
|
let audio = &appstate.audio;
|
||||||
|
|
||||||
let muted = try_w!(audio.get_mute());
|
let muted = try_w!(audio.get_mute());
|
||||||
let _ = try_w!(audio.set_mute(!muted, AudioUserPopup));
|
let _ = try_w!(audio.set_mute(!muted, AudioUser::Popup));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,53 +4,153 @@ use gdk_pixbuf;
|
|||||||
use gdk_sys;
|
use gdk_sys;
|
||||||
use glib;
|
use glib;
|
||||||
use glib_sys;
|
use glib_sys;
|
||||||
|
use gtk;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use libc;
|
use libc;
|
||||||
use audio::AudioUser::*;
|
use audio::*;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
use std::path;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const ICON_MIN_SIZE: i32 = 16;
|
||||||
|
|
||||||
|
|
||||||
// struct VolMeter {
|
// struct VolMeter {
|
||||||
// pub red: u8,
|
// pub red: u8,
|
||||||
// pub green: u8,
|
// pub green: u8,
|
||||||
// pub blue: u8,
|
// pub blue: u8,
|
||||||
// pub x_offset_pct: i64,
|
// pub x_offset_pct: i64,
|
||||||
// pub y_offset_pct: i64,
|
// pub y_offset_pct: i64,
|
||||||
// /* dynamic */
|
// /* dynamic */
|
||||||
// pub pixbuf: gdk_pixbuf::Pixbuf,
|
// pub pixbuf: gdk_pixbuf::Pixbuf,
|
||||||
// pub width: i64,
|
// pub width: i64,
|
||||||
// pub row: u8,
|
// pub row: u8,
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
// impl VolMeter {
|
// impl VolMeter {
|
||||||
// pub fn new() -> VolMeter {
|
// pub fn new() -> VolMeter {
|
||||||
// let pixbux = Pixbuf::new();
|
// let pixbux = Pixbuf::new();
|
||||||
// return VolMeter {
|
// return VolMeter {
|
||||||
// red: 255,
|
// red: 255,
|
||||||
// green: 255,
|
// green: 255,
|
||||||
// blue: 255,
|
// blue: 255,
|
||||||
// x_offset_pct: 0,
|
// x_offset_pct: 0,
|
||||||
// y_offset_pct: 0,
|
// y_offset_pct: 0,
|
||||||
// pixbuf: ,
|
// pixbuf: ,
|
||||||
// width: ,
|
// width: ,
|
||||||
// row: ,
|
// row: ,
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
fn pixbuf_new_from_stock(icon_name: String, size: u32) {
|
// TODO: connect on icon theme change
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct AudioPix {
|
||||||
|
muted: gdk_pixbuf::Pixbuf,
|
||||||
|
low: gdk_pixbuf::Pixbuf,
|
||||||
|
medium: gdk_pixbuf::Pixbuf,
|
||||||
|
high: gdk_pixbuf::Pixbuf,
|
||||||
|
off: gdk_pixbuf::Pixbuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl AudioPix {
|
||||||
|
pub fn new(size: i32) -> Result<AudioPix> {
|
||||||
|
let theme: gtk::IconTheme =
|
||||||
|
gtk::IconTheme::get_default().ok_or("Couldn't get default icon theme")?;
|
||||||
|
let pix = AudioPix {
|
||||||
|
muted: pixbuf_new_from_theme("audio-volume-muted", size, &theme)?,
|
||||||
|
low: pixbuf_new_from_theme("audio-volume-low", size, &theme)?,
|
||||||
|
medium: pixbuf_new_from_theme("audio-volume-medium", size, &theme)?,
|
||||||
|
high: pixbuf_new_from_theme("audio-volume-high", size, &theme)?,
|
||||||
|
/* 'audio-volume-off' is not available in every icon set.
|
||||||
|
* Check freedesktop standard for more info:
|
||||||
|
* http://standards.freedesktop.org/icon-naming-spec/
|
||||||
|
* icon-naming-spec-latest.html
|
||||||
|
*/
|
||||||
|
off: pixbuf_new_from_theme("audio-volume-off", size, &theme).or(pixbuf_new_from_theme("audio-volume-low", size, &theme))?,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(pix);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_pix(&self, vol_level: VolLevel) -> &gdk_pixbuf::Pixbuf {
|
||||||
|
match vol_level {
|
||||||
|
VolLevel::Muted => &self.muted,
|
||||||
|
VolLevel::Low => &self.low,
|
||||||
|
VolLevel::Medium => &self.medium,
|
||||||
|
VolLevel::High => &self.high,
|
||||||
|
VolLevel::Off => &self.off,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn pixbuf_new_from_theme(icon_name: &str,
|
||||||
|
size: i32,
|
||||||
|
theme: >k::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))?;
|
||||||
|
|
||||||
|
debug!("Loading stock icon {} from {:?}",
|
||||||
|
icon_name,
|
||||||
|
icon_info.get_filename().unwrap_or(path::PathBuf::new()));
|
||||||
|
|
||||||
|
// TODO: propagate error
|
||||||
|
let pixbuf = icon_info.load_icon().unwrap();
|
||||||
|
|
||||||
|
return Ok(pixbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn update_tray_icon(audio_pix: &AudioPix, appstate: &AppS) {
|
||||||
|
debug!("Update tray icon");
|
||||||
|
let status_icon = &appstate.gui.status_icon;
|
||||||
|
let pixbuf = audio_pix.select_pix(appstate.audio.vol_level());
|
||||||
|
debug!("VolLevel: {:?}", appstate.audio.vol_level());
|
||||||
|
status_icon.set_from_pixbuf(Some(pixbuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn init_tray_icon(appstate: Rc<AppS>) {
|
pub fn init_tray_icon(appstate: Rc<AppS>) {
|
||||||
|
let audio_pix = Rc::new(RefCell::new(try_w!(AudioPix::new(ICON_MIN_SIZE))));
|
||||||
|
update_tray_icon(&audio_pix.borrow(), &appstate);
|
||||||
|
|
||||||
|
/* connect audio handler */
|
||||||
|
{
|
||||||
|
let _audio_pix = audio_pix.clone();
|
||||||
|
let apps = appstate.clone();
|
||||||
|
appstate.audio.connect_handler(Box::new(move |s, u| {
|
||||||
|
match (s, u) {
|
||||||
|
(AudioSignal::ValuesChanged, _) => {
|
||||||
|
debug!("tray icon handler: {:?} {:?}", s, u);
|
||||||
|
update_tray_icon(&_audio_pix.borrow(), &apps);
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tray_icon.connect_size_changed */
|
||||||
|
{
|
||||||
|
let apps = appstate.clone();
|
||||||
|
let tray_icon = &appstate.gui.status_icon;
|
||||||
|
let _audio_pix = audio_pix.clone();
|
||||||
|
tray_icon.connect_size_changed(move |_, size| on_tray_icon_size_changed(&apps, _audio_pix.as_ref(), size));
|
||||||
|
tray_icon.set_visible(true);
|
||||||
|
}
|
||||||
|
|
||||||
/* tray_icon.connect_activate */
|
/* tray_icon.connect_activate */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
@ -108,13 +208,36 @@ fn on_tray_icon_scroll_event(appstate: &AppS,
|
|||||||
let scroll_dir: gdk::ScrollDirection = event.get_direction();
|
let scroll_dir: gdk::ScrollDirection = event.get_direction();
|
||||||
match scroll_dir {
|
match scroll_dir {
|
||||||
gdk::ScrollDirection::Up => {
|
gdk::ScrollDirection::Up => {
|
||||||
try_wr!(appstate.audio.increase_vol(AudioUserTrayIcon), false);
|
try_wr!(appstate.audio.increase_vol(AudioUser::TrayIcon), false);
|
||||||
}
|
}
|
||||||
gdk::ScrollDirection::Down => {
|
gdk::ScrollDirection::Down => {
|
||||||
try_wr!(appstate.audio.decrease_vol(AudioUserTrayIcon), false);
|
try_wr!(appstate.audio.decrease_vol(AudioUser::TrayIcon), false);
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn on_tray_icon_size_changed(appstate: &AppS,
|
||||||
|
audio_pix: &RefCell<AudioPix>,
|
||||||
|
size: i32)
|
||||||
|
-> bool {
|
||||||
|
debug!("Tray icon size is now {}", size);
|
||||||
|
|
||||||
|
let mut size = size;
|
||||||
|
if size < ICON_MIN_SIZE {
|
||||||
|
size = ICON_MIN_SIZE;
|
||||||
|
debug!("Forcing size to the minimum value {}", size);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut pix = audio_pix.borrow_mut();
|
||||||
|
*pix = try_wr!(AudioPix::new(size), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
update_tray_icon(&audio_pix.borrow(), &appstate);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user