Merge branch 'audio_trait'

This commit is contained in:
Julian Ospald 2017-07-19 02:23:15 +02:00
commit a47bede4a7
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
13 changed files with 573 additions and 546 deletions

View File

@ -1,3 +1,5 @@
#![allow(illegal_floating_point_literal_pattern)]
//! Alsa audio subsystem.
//!
//! This mod mainly defines the `AlsaCard` struct, which is the only data
@ -10,17 +12,21 @@ use alsa::mixer::SelemChannelId::*;
use alsa::mixer::{Mixer, Selem, SelemId};
use alsa::poll::PollDescriptors;
use alsa_sys;
use audio::*;
use errors::*;
use glib;
use glib_sys;
use libc::c_uint;
use libc::pollfd;
use libc::size_t;
use std::cell::Cell;
use std::cell::RefCell;
use std::mem;
use std::ptr;
use std::rc::Rc;
use std::u8;
use support_alsa::*;
use support_audio::*;
@ -67,8 +73,6 @@ impl AlsaCard {
/// If an elem name is provided, it will be tried. If `None` is provided
/// or the given elem name does not exist or is not playable, any other
/// playable elem is tried.
/// ## `cb`
/// Callback for the various `AlsaEvent`s.
///
/// # Returns
///
@ -122,11 +126,11 @@ impl AlsaCard {
let acard = Box::new(AlsaCard {
_cannot_construct: (),
card: card,
mixer: mixer,
selem_id: selem_id,
card,
mixer,
selem_id,
watch_ids: Cell::new(vec![]),
cb: cb,
cb,
});
let watch_ids = AlsaCard::watch_poll_descriptors(vec_pollfd,
@ -136,89 +140,13 @@ impl AlsaCard {
return Ok(acard);
}
/// Get the name of the alsa card.
pub fn card_name(&self) -> Result<String> {
return self.card.get_name().from_err();
}
/// Get the name of the channel.
pub fn chan_name(&self) -> Result<String> {
let n = self.selem_id
.get_name()
.map(|y| String::from(y))?;
return Ok(n);
}
/// Get the `Selem`, looked up by the `SelemId`.
pub fn selem(&self) -> Selem {
return self.mixer.find_selem(&self.selem_id).unwrap();
fn selem(&self) -> Selem {
let selem_id = &self.selem_id;
let selem = self.mixer.find_selem(selem_id);
return selem.unwrap();
}
/// Get the current volume. The returned value corresponds to the
/// volume range and might need to be interpreted (such as converting
/// to percentage). This always gets
/// the volume of the `FrontRight` channel, because the seems to be
/// the safest bet.
pub fn get_vol(&self) -> Result<i64> {
let selem = self.selem();
let volume = selem.get_playback_volume(FrontRight);
return volume.from_err();
}
/// Sets the volume of the current card configuration.
/// ## `new_vol`
/// The volume corresponding to the volume range of the `Selem`. This
/// might need to be translated properly first from other formats
/// (like percentage).
pub fn set_vol(&self, new_vol: i64) -> Result<()> {
let selem = self.selem();
return selem.set_playback_volume_all(new_vol).from_err();
}
/// Gets the volume range of the currently selected card configuration.
///
/// # Returns
///
/// `(min, max)`
pub fn get_volume_range(&self) -> (i64, i64) {
let selem = self.selem();
return selem.get_playback_volume_range();
}
/// Whether the current card configuration can be muted.
pub fn has_mute(&self) -> bool {
let selem = self.selem();
return selem.has_playback_switch();
}
/// Get the mute state of the current card configuration.
pub fn get_mute(&self) -> Result<bool> {
let selem = self.selem();
let val = selem.get_playback_switch(FrontRight)?;
return Ok(val == 0);
}
/// Set the mute state of the current card configuration.
/// ## `mute`
/// Passing `true` here means the card will be muted.
pub fn set_mute(&self, mute: bool) -> Result<()> {
let selem = self.selem();
/* true -> mute, false -> unmute */
let _ = selem.set_playback_switch_all(!mute as i32)?;
return Ok(());
}
/// Watch the given alsa card poll descriptors and
/// return the corresponding watch IDs for saving
/// in the `AlsaCard` struct.
@ -248,7 +176,6 @@ impl AlsaCard {
return watch_ids;
}
/// Unwatch the given poll descriptors.
fn unwatch_poll_descriptors(watch_ids: &Vec<u32>) {
for watch_id in watch_ids {
@ -269,6 +196,269 @@ impl Drop for AlsaCard {
}
/// Alsa implementation of the `AudioFrontend`.
pub struct AlsaBackend {
_cannot_construct: (),
/// The alsa card.
pub acard: RefCell<Box<AlsaCard>>,
/// Last timestamp of an internal action we triggered, e.g.
/// by setting the volume or the mute state.
pub last_action_timestamp: Rc<RefCell<i64>>,
/// A set of handlers that react to audio signals. We can
/// connect to these.
pub handlers: Handlers,
/// The step at which to increase/decrease the volume.
/// This value is basically from the preferences.
pub scroll_step: Cell<u32>,
}
impl AlsaBackend {
/// Creates the `AlsaBackend`, containing an `AlsaCard`
/// and additional information.
pub fn new(card_name: Option<String>,
elem_name: Option<String>)
-> Result<AlsaBackend> {
let last_action_timestamp = Rc::new(RefCell::new(0));
let handlers = Handlers::new();
let cb = {
let myhandler = handlers.clone();
let ts = last_action_timestamp.clone();
Rc::new(move |event| {
on_alsa_event(&mut *ts.borrow_mut(),
&myhandler.borrow(),
event)
})
};
let acard = AlsaCard::new(card_name, elem_name, cb);
if acard.is_err() {
invoke_handlers(&handlers.borrow(),
AudioSignal::NoCard,
AudioUser::Unknown);
} else {
invoke_handlers(&handlers.borrow(),
AudioSignal::CardInitialized,
AudioUser::Unknown);
}
let alsa_backend = AlsaBackend {
_cannot_construct: (),
acard: RefCell::new(acard?),
last_action_timestamp: last_action_timestamp.clone(),
handlers,
scroll_step: Cell::new(5),
};
return Ok(alsa_backend);
}
/// Gets the volume range of the currently selected card configuration.
///
/// # Returns
///
/// `(min, max)`
fn get_volume_range(&self) -> (i64, i64) {
let acard = self.acard.borrow();
let selem = acard.selem();
return selem.get_playback_volume_range();
}
}
impl AudioFrontend for AlsaBackend {
fn switch_card(&self,
card_name: Option<String>,
elem_name: Option<String>,
user: AudioUser)
-> Result<()> {
debug!("Switching cards");
debug!("Old card name: {}", self.card_name().unwrap());
debug!("Old chan name: {}", self.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)?;
}
invoke_handlers(&self.handlers.borrow(),
AudioSignal::CardInitialized,
user);
return Ok(());
}
fn card_name(&self) -> Result<String> {
return self.acard
.borrow()
.card
.get_name()
.from_err();
}
fn chan_name(&self) -> Result<String> {
let n = self.acard
.borrow()
.selem_id
.get_name()
.map(|y| String::from(y))?;
return Ok(n);
}
fn playable_chan_names(&self) -> Vec<String> {
return get_playable_selem_names(&self.acard.borrow().mixer);
}
fn get_vol(&self) -> Result<f64> {
let acard = self.acard.borrow();
let selem = acard.selem();
let volume = selem.get_playback_volume(FrontRight)?;
return vol_to_percent(volume, self.get_volume_range());
}
fn set_vol(&self,
new_vol: f64,
user: AudioUser,
dir: VolDir,
auto_unmute: bool)
-> Result<()> {
{
let mut rc = self.last_action_timestamp.borrow_mut();
*rc = glib::get_monotonic_time();
}
let alsa_vol = percent_to_vol(new_vol, self.get_volume_range(), dir)?;
/* only invoke handlers etc. if volume did actually change */
{
let old_alsa_vol =
percent_to_vol(self.get_vol()?, self.get_volume_range(), dir)?;
if old_alsa_vol == alsa_vol {
return Ok(());
}
}
/* auto-unmute */
if auto_unmute && self.has_mute() && self.get_mute()? {
self.set_mute(false, user)?;
}
debug!("Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
self.card_name().unwrap(),
self.chan_name().unwrap(),
new_vol,
user);
self.acard
.borrow()
.selem()
.set_playback_volume_all(alsa_vol)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(());
}
fn vol_level(&self) -> VolLevel {
let muted = self.get_mute().unwrap_or(false);
if muted {
return VolLevel::Muted;
}
let cur_vol = try_r!(self.get_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,
}
}
fn increase_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()> {
let old_vol = self.get_vol()?;
let new_vol = old_vol + (self.scroll_step.get() as f64);
return self.set_vol(new_vol, user, VolDir::Up, auto_unmute);
}
fn decrease_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()> {
let old_vol = self.get_vol()?;
let new_vol = old_vol - (self.scroll_step.get() as f64);
return self.set_vol(new_vol, user, VolDir::Down, auto_unmute);
}
fn has_mute(&self) -> bool {
let acard = self.acard.borrow();
let selem = acard.selem();
return selem.has_playback_switch();
}
fn get_mute(&self) -> Result<bool> {
let acard = self.acard.borrow();
let selem = acard.selem();
let val = selem.get_playback_switch(FrontRight)?;
return Ok(val == 0);
}
fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
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.card_name().unwrap(),
self.chan_name().unwrap(),
user);
let acard = self.acard.borrow();
let selem = acard.selem();
/* true -> mute, false -> unmute */
let _ = selem.set_playback_switch_all(!mute as i32)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(());
}
fn toggle_mute(&self, user: AudioUser) -> Result<()> {
let muted = self.get_mute()?;
return self.set_mute(!muted, user);
}
fn connect_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
self.handlers.add_handler(cb);
}
fn set_scroll_step(&self, scroll_step: u32) {
self.scroll_step.set(scroll_step);
}
fn get_scroll_step(&self) -> u32 {
return self.scroll_step.get();
}
}
/// The C callback function registered in `watch_poll_descriptors()`.
extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
cond: glib_sys::GIOCondition,
@ -323,3 +513,61 @@ extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
return true as glib_sys::gboolean;
}
/// Invokes the registered handlers.
fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
signal: AudioSignal,
user: AudioUser) {
debug!("Invoking handlers for signal {:?} by user {:?}",
signal,
user);
if handlers.is_empty() {
debug!("No handler found");
} else {
debug!("Executing handlers")
}
for handler in handlers {
let unboxed = handler.as_ref();
unboxed(signal, user);
}
}
/// The callback for alsa events that is passed to the alsa subsystem.
/// This is the bridge between low-level alsa events and "high-level"
/// audio system signals.
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 {
let now: i64 = glib::get_monotonic_time();
let delay: i64 = now - last;
if delay < 1000000 {
return;
}
debug!("Discarding last time stamp, too old");
*last_action_timestamp = 0;
}
/* external change */
match alsa_event {
AlsaEvent::AlsaCardError => {
invoke_handlers(handlers,
self::AudioSignal::CardError,
self::AudioUser::Unknown);
}
AlsaEvent::AlsaCardDiconnected => {
invoke_handlers(handlers,
self::AudioSignal::CardDisconnected,
self::AudioUser::Unknown);
}
AlsaEvent::AlsaCardValuesChanged => {
invoke_handlers(handlers,
self::AudioSignal::ValuesChanged,
self::AudioUser::Unknown);
}
}
}

View File

@ -1,7 +1,8 @@
//! Global application state.
use audio::{Audio, AudioUser};
use alsa_card::*;
use audio::*;
use errors::*;
use gtk;
use hotkeys::Hotkeys;
@ -18,12 +19,14 @@ use notif::*;
// TODO: destructors
/// The global application state struct.
pub struct AppS {
pub struct AppS<T>
where T: AudioFrontend
{
_cant_construct: (),
/// Mostly static GUI state.
pub gui: Gui,
/// Audio state.
pub audio: Rc<Audio>,
pub audio: Rc<T>,
/// Preferences state.
pub prefs: RefCell<Prefs>,
#[cfg(feature = "notify")]
@ -31,22 +34,14 @@ pub struct AppS {
/// is set to `None`.
pub notif: Option<Notif>,
/// Hotkey state.
pub hotkeys: RefCell<Box<Hotkeys>>, // Gets an Rc to Audio.
pub hotkeys: RefCell<Box<Hotkeys<T>>>, // Gets an Rc to Audio.
}
impl AppS {
/// Create an application state instance. There should really only be one.
pub fn new() -> AppS {
let builder_popup_window =
gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"),
"/data/ui/popup-window.glade")));
let builder_popup_menu =
gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"),
"/data/ui/popup-menu.glade")));
/// Create a new application state using the `AlsaBackend`.
pub fn new_alsa_appstate() -> AppS<AlsaBackend> {
let prefs = RefCell::new(unwrap_error!(Prefs::new(), None));
let card_name = prefs.borrow()
.device_prefs
.card
@ -55,14 +50,33 @@ impl AppS {
.device_prefs
.channel
.clone();
let audio = Rc::new(unwrap_error!(AlsaBackend::new(Some(card_name),
Some(chan_name)),
None));
return AppS::new(prefs, audio);
}
impl<T> AppS<T>
where T: AudioFrontend
{
/// Create an application state instance. There should really only be one.
pub fn new(prefs: RefCell<Prefs>, audio: Rc<T>) -> Self {
let builder_popup_window =
gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"),
"/data/ui/popup-window.glade")));
let builder_popup_menu =
gtk::Builder::new_from_string(include_str!(concat!(env!("CARGO_MANIFEST_DIR"),
"/data/ui/popup-menu.glade")));
// TODO: better error handling
#[cfg(feature = "notify")]
let notif = result_warn!(Notif::new(&prefs.borrow()), None).ok();
let audio = Rc::new(unwrap_error!(Audio::new(Some(card_name),
Some(chan_name)),
None));
let hotkeys = unwrap_error!(wresult_warn!(Hotkeys::new(&prefs.borrow(),
let hotkeys = unwrap_error!(wresult_warn!(
Hotkeys::new(&prefs.borrow(),
audio.clone()), None),
None);
@ -72,7 +86,7 @@ impl AppS {
return AppS {
_cant_construct: (),
gui,
audio: audio,
audio,
prefs,
#[cfg(feature = "notify")]
notif,
@ -87,14 +101,14 @@ impl AppS {
pub fn update_tray_icon(&self) -> Result<()> {
debug!("Update tray icon!");
return self.gui.tray_icon.update_all(&self.prefs.borrow(),
&self.audio,
self.audio.as_ref(),
None);
}
/// Update the Popup Window state.
pub fn update_popup_window(&self) -> Result<()> {
debug!("Update PopupWindow!");
return self.gui.popup_window.update(&self.audio);
return self.gui.popup_window.update(self.audio.as_ref());
}
#[cfg(feature = "notify")]
@ -112,7 +126,7 @@ impl AppS {
/// Update the audio state.
pub fn update_audio(&self, user: AudioUser) -> Result<()> {
return audio_reload(&self.audio, &self.prefs.borrow(), user);
return audio_reload(self.audio.as_ref(), &self.prefs.borrow(), user);
}
/// Update the config file.

View File

@ -1,5 +1,4 @@
#![allow(missing_docs)] // enums
#![allow(illegal_floating_point_literal_pattern)]
//! High-level audio subsystem.
//!
@ -9,15 +8,11 @@
//! of the underlying audio implementation, may it be alsa or whatever.
use alsa_card::*;
use errors::*;
use glib;
use std::cell::Cell;
use std::cell::Ref;
use std::cell::RefCell;
use std::f64;
use std::rc::Rc;
use support_alsa::*;
use support_audio::*;
@ -65,162 +60,39 @@ pub struct Handlers {
impl Handlers {
fn new() -> Handlers {
pub fn new() -> Handlers {
return Handlers { inner: Rc::new(RefCell::new(vec![])) };
}
fn borrow(&self) -> Ref<Vec<Box<Fn(AudioSignal, AudioUser)>>> {
pub fn borrow(&self) -> Ref<Vec<Box<Fn(AudioSignal, AudioUser)>>> {
return self.inner.borrow();
}
fn add_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
pub fn add_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
self.inner.borrow_mut().push(cb);
}
}
/// High-level Audio struct, which could theoretically be backend
/// agnostic.
pub struct Audio {
_cannot_construct: (),
/// The alsa card.
pub acard: RefCell<Box<AlsaCard>>,
/// Last timestamp of an internal action we triggered, e.g.
/// by setting the volume or the mute state.
pub last_action_timestamp: Rc<RefCell<i64>>,
/// A set of handlers that react to audio signals. We can
/// connect to these.
pub handlers: Handlers,
/// The step at which to increase/decrease the volume.
/// This value is basically from the preferences.
pub scroll_step: Cell<u32>,
}
impl Audio {
/// Create a new Audio instance. This tries very hard to get
/// a working configuration from the backend.
/// ## `card_name`
/// If a card name is provided, it will be tried. If `None` is provided
/// or the given card name does not exist or is not playable, any other
/// playable card is tried.
/// ## `elem_name`
/// If an elem name is provided, it will be tried. If `None` is provided
/// or the given elem name does not exist or is not playable, any other
/// playable elem is tried.
///
/// # Returns
///
/// `Ok(Audio)` on success, `Err(error)` otherwise.
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));
let cb = {
let myhandler = handlers.clone();
let ts = last_action_timestamp.clone();
Rc::new(move |event| {
on_alsa_event(&mut *ts.borrow_mut(),
&myhandler.borrow(),
event)
})
};
let acard = AlsaCard::new(card_name, elem_name, cb);
/* additionally dispatch signals */
if acard.is_err() {
invoke_handlers(&handlers.borrow(),
AudioSignal::NoCard,
AudioUser::Unknown);
} else {
invoke_handlers(&handlers.borrow(),
AudioSignal::CardInitialized,
AudioUser::Unknown);
}
let audio = Audio {
_cannot_construct: (),
acard: RefCell::new(acard?),
last_action_timestamp: last_action_timestamp.clone(),
handlers: handlers.clone(),
scroll_step: Cell::new(5),
};
return Ok(audio);
}
/// Switches the current alsa card. Behaves the same way in regards to
/// `card_name` and `elem_name` as the `Audio::new()` method.
// TODO: explain more, specify details that need to be implemented
pub trait AudioFrontend {
/// Switches the current card. Must invoke handlers.
/// ## `user`
/// Where the card switch originates from.
pub fn switch_acard(&self,
fn switch_card(&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();
{
let mut ac = self.acard.borrow_mut();
*ac = AlsaCard::new(card_name, elem_name, cb)?;
}
-> Result<()>;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::CardInitialized,
user);
/// Current volume. Between 0 and 100.
/// This always gets the volume of the `FrontRight` channel, because the
/// seems to be the safest bet.
fn get_vol(&self) -> Result<f64>;
return Ok(());
}
/// Current volume.
pub fn vol(&self) -> Result<f64> {
let alsa_vol = self.acard
.borrow()
.get_vol()?;
return vol_to_percent(alsa_vol, self.acard.borrow().get_volume_range());
}
/// Current volume level, nicely usable for e.g. selecting from a set
/// of images.
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,
}
}
/// Set the current volume.
/// Set the current volume. Must invoke handlers.
/// ## `new_vol`
/// Set the volume to this value.
/// ## `user`
@ -230,219 +102,60 @@ impl Audio {
/// or increase. This helps with rounding problems.
/// ## `auto_unmute`
/// Whether to automatically unmute if the volume changes.
pub fn set_vol(&self,
fn set_vol(&self,
new_vol: f64,
user: AudioUser,
dir: VolDir,
auto_unmute: bool)
-> Result<()> {
{
let mut rc = self.last_action_timestamp.borrow_mut();
*rc = glib::get_monotonic_time();
}
let alsa_vol = percent_to_vol(new_vol,
self.acard.borrow().get_volume_range(),
dir)?;
/* only invoke handlers etc. if volume did actually change */
{
let old_alsa_vol =
percent_to_vol(self.vol()?,
self.acard.borrow().get_volume_range(),
dir)?;
if old_alsa_vol == alsa_vol {
return Ok(());
}
}
/* auto-unmute */
if auto_unmute && self.has_mute() && self.get_mute()? {
self.set_mute(false, user)?;
}
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(alsa_vol)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(());
}
-> Result<()>;
/// Current volume level, nicely usable for e.g. selecting from a set
/// of images.
fn vol_level(&self) -> VolLevel;
/// Increase the volume. The step to increasy by is taken from
/// `self.scroll_step`.
/// ## `user`
/// Where the card switch originates from.
pub fn increase_vol(&self,
user: AudioUser,
auto_unmute: bool)
-> Result<()> {
let old_vol = self.vol()?;
let new_vol = old_vol + (self.scroll_step.get() as f64);
return self.set_vol(new_vol, user, VolDir::Up, auto_unmute);
}
fn increase_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()>;
/// Decrease the volume. The step to decrease by is taken from
/// `self.scroll_step`.
/// ## `user`
/// Where the card switch originates from.
pub fn decrease_vol(&self,
user: AudioUser,
auto_unmute: bool)
-> Result<()> {
let old_vol = self.vol()?;
let new_vol = old_vol - (self.scroll_step.get() as f64);
return self.set_vol(new_vol, user, VolDir::Down, auto_unmute);
}
fn decrease_vol(&self, user: AudioUser, auto_unmute: bool) -> Result<()>;
/// Whether the current audio configuration can be muted.
pub fn has_mute(&self) -> bool {
return self.acard.borrow().has_mute();
}
fn has_mute(&self) -> bool;
/// Get the mute state of the current audio configuration.
pub fn get_mute(&self) -> Result<bool> {
return self.acard.borrow().get_mute();
}
fn get_mute(&self) -> Result<bool>;
/// Set the mute state of the current audio configuration.
pub fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()> {
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);
self.acard
.borrow()
.set_mute(mute)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(());
}
/// Must invoke handlers.
fn set_mute(&self, mute: bool, user: AudioUser) -> Result<()>;
/// Toggle the mute state of the current audio configuration.
pub fn toggle_mute(&self, user: AudioUser) -> Result<()> {
let muted = self.get_mute()?;
return self.set_mute(!muted, user);
}
fn toggle_mute(&self, user: AudioUser) -> Result<()>;
/// Connect a signal handler to the audio subsystem. This can
/// be done from anywhere, e.g. in the UI code to react to
/// certain signals. Multiple handlers for the same signals are fine,
/// they will be executed in order.
pub fn connect_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>) {
self.handlers.add_handler(cb);
}
fn connect_handler(&self, cb: Box<Fn(AudioSignal, AudioUser)>);
/// Get the current card name.
pub fn card_name(&self) -> Result<String> {
return self.acard.borrow().card_name();
}
fn card_name(&self) -> Result<String>;
/// Get the currently playable channel names.
pub fn playable_chan_names(&self) -> Vec<String> {
return get_playable_selem_names(&self.acard.borrow().mixer);
}
fn playable_chan_names(&self) -> Vec<String>;
/// Get the current active channel name.
pub fn chan_name(&self) -> Result<String> {
return self.acard.borrow().chan_name();
}
}
/// Invokes the registered handlers.
fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
signal: AudioSignal,
user: AudioUser) {
debug!("Invoking handlers for signal {:?} by user {:?}",
signal,
user);
if handlers.is_empty() {
debug!("No handler found");
} else {
debug!("Executing handlers")
}
for handler in handlers {
let unboxed = handler.as_ref();
unboxed(signal, user);
}
}
/// The callback for alsa events that is passed to the alsa subsystem.
/// This is the bridge between low-level alsa events and "high-level"
/// audio system signals.
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 {
let now: i64 = glib::get_monotonic_time();
let delay: i64 = now - last;
if delay < 1000000 {
return;
}
debug!("Discarding last time stamp, too old");
*last_action_timestamp = 0;
}
/* external change */
match alsa_event {
AlsaEvent::AlsaCardError => {
invoke_handlers(handlers,
self::AudioSignal::CardError,
self::AudioUser::Unknown);
}
AlsaEvent::AlsaCardDiconnected => {
invoke_handlers(handlers,
self::AudioSignal::CardDisconnected,
self::AudioUser::Unknown);
}
AlsaEvent::AlsaCardValuesChanged => {
invoke_handlers(handlers,
self::AudioSignal::ValuesChanged,
self::AudioUser::Unknown);
}
}
fn chan_name(&self) -> Result<String>;
/// Set the scroll step.
fn set_scroll_step(&self, scroll_step: u32);
/// Get the scroll step.
fn get_scroll_step(&self) -> u32;
}

View File

@ -18,7 +18,7 @@ fn main() {
.init(Some("pnmixer=debug".to_string()))
.unwrap_or_else(|e| panic!("Logger initialization failed with {}", e));
let apps = Rc::new(AppS::new());
let apps = Rc::new(new_alsa_appstate());
ui_entry::init(apps);

View File

@ -5,7 +5,7 @@
//! before they can be interpreted by Gtk/Gdk.
use audio::{Audio, AudioUser};
use audio::*;
use errors::*;
use errors;
use gdk;
@ -23,22 +23,26 @@ use x11;
/// The possible Hotkeys for manipulating the volume.
pub struct Hotkeys {
pub struct Hotkeys<T>
where T: AudioFrontend
{
enabled: bool,
mute_key: Option<Hotkey>,
up_key: Option<Hotkey>,
down_key: Option<Hotkey>,
// need this to access audio in 'key_filter'
audio: Rc<Audio>,
audio: Rc<T>,
auto_unmute: bool,
}
impl Hotkeys {
impl<T> Hotkeys<T>
where T: AudioFrontend
{
/// Creates the hotkeys subsystem and binds the hotkeys.
pub fn new(prefs: &Prefs,
audio: Rc<Audio>)
-> WResult<Box<Hotkeys>, errors::Error, errors::Error> {
audio: Rc<T>)
-> WResult<Box<Hotkeys<T>>, errors::Error, errors::Error> {
debug!("Creating hotkeys control");
let mut hotkeys =
Box::new(Hotkeys {
@ -55,10 +59,10 @@ impl Hotkeys {
/* bind hotkeys */
let data_ptr =
unsafe {
mem::transmute::<&Hotkeys,
mem::transmute::<&Hotkeys<T>,
glib_sys::gpointer>(hotkeys.as_ref())
};
hotkeys_add_filter(Some(key_filter), data_ptr);
hotkeys_add_filter(Some(key_filter::<T>), data_ptr);
return WOk(hotkeys, warn);
}
@ -159,8 +163,8 @@ impl Hotkeys {
}
let data_ptr =
unsafe { mem::transmute::<&Hotkeys, glib_sys::gpointer>(self) };
hotkeys_add_filter(Some(key_filter), data_ptr);
unsafe { mem::transmute::<&Hotkeys<T>, glib_sys::gpointer>(self) };
hotkeys_add_filter(Some(key_filter::<T>), data_ptr);
}
/// Unbind hotkeys manually. Should be paired with a `bind()` call.
@ -186,22 +190,25 @@ impl Hotkeys {
}
let data_ptr =
unsafe { mem::transmute::<&Hotkeys, glib_sys::gpointer>(self) };
hotkeys_remove_filter(Some(key_filter), data_ptr);
unsafe { mem::transmute::<&Hotkeys<T>, glib_sys::gpointer>(self) };
hotkeys_remove_filter(Some(key_filter::<T>), data_ptr);
}
}
impl Drop for Hotkeys {
impl<T> Drop for Hotkeys<T>
where T: AudioFrontend
{
fn drop(&mut self) {
debug!("Freeing hotkeys");
self.mute_key = None;
self.up_key = None;
self.down_key = None;
let data_ptr =
unsafe { mem::transmute::<&mut Hotkeys, glib_sys::gpointer>(self) };
let data_ptr = unsafe {
mem::transmute::<&mut Hotkeys<T>, glib_sys::gpointer>(self)
};
hotkeys_remove_filter(Some(key_filter), data_ptr)
hotkeys_remove_filter(Some(key_filter::<T>), data_ptr)
}
}
@ -243,14 +250,16 @@ fn hotkeys_remove_filter(function: gdk_sys::GdkFilterFunc,
/// This function is called before Gtk/Gdk can respond
/// to any(!) window event and handles pressed hotkeys.
extern "C" fn key_filter(gdk_xevent: *mut gdk_sys::GdkXEvent,
extern "C" fn key_filter<T>(gdk_xevent: *mut gdk_sys::GdkXEvent,
_: *mut gdk_sys::GdkEvent,
data: glib_sys::gpointer)
-> gdk_sys::GdkFilterReturn {
-> gdk_sys::GdkFilterReturn
where T: AudioFrontend
{
let xevent = gdk_xevent as *mut x11::xlib::XKeyEvent;
let hotkeys: &Hotkeys =
unsafe { mem::transmute::<glib_sys::gpointer, &Hotkeys>(data) };
let hotkeys: &Hotkeys<T> =
unsafe { mem::transmute::<glib_sys::gpointer, &Hotkeys<T>>(data) };
let mute_key = &hotkeys.mute_key;
let up_key = &hotkeys.up_key;
let down_key = &hotkeys.down_key;

View File

@ -97,4 +97,3 @@ pub mod ui_tray_icon;
#[cfg(feature = "notify")]
pub mod notif;

View File

@ -73,8 +73,10 @@ impl Notif {
}
/// Shows a volume notification, e.g. for volume or mute state change.
pub fn show_volume_notif(&self, audio: &Audio) -> Result<()> {
let vol = audio.vol()?;
pub fn show_volume_notif<T>(&self, audio: &T) -> Result<()>
where T: AudioFrontend
{
let vol = audio.get_vol()?;
let vol_level = audio.vol_level();
let icon = {
@ -92,12 +94,8 @@ impl Notif {
VolLevel::Muted => String::from("Volume muted"),
_ => {
format!("{} ({})\nVolume: {}",
audio.acard
.borrow()
.card_name()?,
audio.acard
.borrow()
.chan_name()?,
audio.card_name()?,
audio.chan_name()?,
vol as i32)
}
}
@ -133,7 +131,9 @@ impl Drop for Notif {
/// Initialize the notification subsystem.
pub fn init_notify(appstate: Rc<AppS>) {
pub fn init_notify<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
{
/* connect handler */
let apps = appstate.clone();
@ -154,16 +154,16 @@ pub fn init_notify(appstate: Rc<AppS>) {
(AudioSignal::CardError, _, _) => (),
(AudioSignal::ValuesChanged,
AudioUser::TrayIcon,
(_, true, _, _)) => try_w!(notif.show_volume_notif(&apps.audio)),
(_, true, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
(AudioSignal::ValuesChanged,
AudioUser::Popup,
(true, _, _, _)) => try_w!(notif.show_volume_notif(&apps.audio)),
(true, _, _, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
(AudioSignal::ValuesChanged,
AudioUser::Unknown,
(_, _, true, _)) => try_w!(notif.show_volume_notif(&apps.audio)),
(_, _, true, _)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
(AudioSignal::ValuesChanged,
AudioUser::Hotkeys,
(_, _, _, true)) => try_w!(notif.show_volume_notif(&apps.audio)),
(_, _, _, true)) => try_w!(notif.show_volume_notif(apps.audio.as_ref())),
_ => (),
}
}));

View File

@ -6,7 +6,7 @@
//! but are important helpers.
use audio::{Audio, AudioUser};
use audio::*;
use errors::*;
use prefs::*;
use support_alsa::*;
@ -54,14 +54,13 @@ pub fn lrint(v: f64, dir: VolDir) -> f64 {
/// Reload the audio system.
pub fn audio_reload(audio: &Audio,
prefs: &Prefs,
user: AudioUser)
-> Result<()> {
pub fn audio_reload<T>(audio: &T, prefs: &Prefs, user: AudioUser) -> Result<()>
where T: AudioFrontend
{
let card = &prefs.device_prefs.card;
let channel = &prefs.device_prefs.channel;
// TODO: is this clone safe?
return audio.switch_acard(Some(card.clone()), Some(channel.clone()), user);
return audio.switch_card(Some(card.clone()), Some(channel.clone()), user);
}

View File

@ -2,7 +2,7 @@
use app_state::*;
use audio::{AudioUser, AudioSignal};
use audio::*;
use gtk::DialogExt;
use gtk::MessageDialogExt;
use gtk::WidgetExt;
@ -56,20 +56,22 @@ impl Gui {
/// Initialize the GUI system.
pub fn init(appstate: Rc<AppS>) {
pub fn init<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
{
/* "global" audio signal handler */
let apps = appstate.clone();
appstate.audio.connect_handler(
Box::new(move |s, u| match (s, u) {
(AudioSignal::CardDisconnected, _) => {
try_w!(audio_reload(&apps.audio,
try_w!(audio_reload(apps.audio.as_ref(),
&apps.prefs.borrow(),
AudioUser::Unknown));
},
(AudioSignal::CardError, _) => {
if run_audio_error_dialog(&apps.gui.popup_menu.menu_window) == (GTK_RESPONSE_YES as i32) {
try_w!(audio_reload(&apps.audio,
try_w!(audio_reload(apps.audio.as_ref(),
&apps.prefs.borrow(),
AudioUser::Unknown));
}

View File

@ -12,7 +12,7 @@
//! * Quit
use app_state::*;
use audio::AudioUser;
use audio::*;
use gtk::prelude::*;
use gtk;
use std::rc::Rc;
@ -40,7 +40,9 @@ create_builder_item!(PopupMenu,
/// Initialize the popup menu subsystem, registering all callbacks.
pub fn init_popup_menu(appstate: Rc<AppS>) {
pub fn init_popup_menu<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
/* audio.connect_handler */
{
let apps = appstate.clone();
@ -113,7 +115,7 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
let apps = appstate.clone();
let reload_item = &appstate.gui.popup_menu.reload_item;
reload_item.connect_activate(move |_| {
try_w!(audio_reload(&apps.audio,
try_w!(audio_reload(apps.audio.as_ref(),
&apps.prefs.borrow(),
AudioUser::Popup))
});
@ -129,7 +131,9 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
/// When the about menu item is activated.
fn on_about_item_activate(appstate: &AppS) {
fn on_about_item_activate<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let popup_menu = &appstate.gui.popup_menu.menu_window;
let about_dialog = create_about_dialog();
about_dialog.set_skip_taskbar_hint(true);
@ -171,14 +175,18 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.",
/// When the Preferences item is activated.
fn on_prefs_item_activate(appstate: &Rc<AppS>) {
fn on_prefs_item_activate<T>(appstate: &Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
/* TODO: only create if needed */
show_prefs_dialog(appstate);
}
/// When the Mute item is checked.
fn set_mute_check(apps: &Rc<AppS>) {
fn set_mute_check<T>(apps: &Rc<AppS<T>>)
where T: AudioFrontend
{
let mute_check = &apps.gui.popup_menu.mute_check;
let m_muted = apps.audio.get_mute();
match m_muted {

View File

@ -62,17 +62,21 @@ impl PopupWindow {
/// Update the popup window state, including the slider
/// and the mute checkbutton.
pub fn update(&self, audio: &Audio) -> Result<()> {
let cur_vol = audio.vol()?;
pub fn update<T>(&self, audio: &T) -> Result<()>
where T: AudioFrontend
{
let cur_vol = audio.get_vol()?;
set_slider(&self.vol_scale_adj, cur_vol);
self.update_mute_check(&audio);
self.update_mute_check(audio);
return Ok(());
}
/// Update the mute checkbutton.
pub fn update_mute_check(&self, audio: &Audio) {
pub fn update_mute_check<T>(&self, audio: &T)
where T: AudioFrontend
{
let m_muted = audio.get_mute();
glib::signal_handler_block(&self.mute_check, self.toggle_signal.get());
@ -109,7 +113,9 @@ impl PopupWindow {
/// Initialize the popup window subsystem.
pub fn init_popup_window(appstate: Rc<AppS>) {
pub fn init_popup_window<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
/* audio.connect_handler */
{
let apps = appstate.clone();
@ -130,11 +136,12 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
* and not the real value reported by the audio system.
*/
(_, AudioUser::Popup) => {
apps.gui.popup_window.update_mute_check(&apps.audio);
apps.gui.popup_window.update_mute_check(apps.audio
.as_ref());
}
/* external change, safe to update slider too */
(_, _) => {
try_w!(apps.gui.popup_window.update(&apps.audio));
try_w!(apps.gui.popup_window.update(apps.audio.as_ref()));
}
}
}));
@ -213,12 +220,14 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
/// When the popup window is shown.
fn on_popup_window_show(appstate: &AppS) {
fn on_popup_window_show<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let popup_window = &appstate.gui.popup_window;
appstate.gui.popup_window.set_vol_increment(&appstate.prefs.borrow());
glib::signal_handler_block(&popup_window.vol_scale_adj,
popup_window.changed_signal.get());
try_w!(appstate.gui.popup_window.update(&appstate.audio));
try_w!(appstate.gui.popup_window.update(appstate.audio.as_ref()));
glib::signal_handler_unblock(&popup_window.vol_scale_adj,
popup_window.changed_signal.get());
popup_window.vol_scale.grab_focus();
@ -257,9 +266,11 @@ fn on_popup_window_event(w: &gtk::Window, e: &gdk::Event) -> gtk::Inhibit {
/// When the volume scale slider is moved.
fn on_vol_scale_value_changed(appstate: &AppS) {
fn on_vol_scale_value_changed<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let audio = &appstate.audio;
let old_vol = try_w!(audio.vol());
let old_vol = try_w!(audio.get_vol());
let val = appstate.gui
.popup_window
@ -279,7 +290,9 @@ fn on_vol_scale_value_changed(appstate: &AppS) {
/// When the mute checkbutton is toggled.
fn on_mute_check_toggled(appstate: &AppS) {
fn on_mute_check_toggled<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let audio = &appstate.audio;
try_w!(audio.toggle_mute(AudioUser::Popup))
}

View File

@ -3,7 +3,7 @@
use app_state::*;
use audio::{AudioUser, AudioSignal};
use audio::*;
use errors::*;
use gdk;
use gtk::ResponseType;
@ -334,7 +334,9 @@ impl PrefsDialog {
/// Show the preferences dialog. This is created and destroyed dynamically
/// and not persistent across the application lifetime.
pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
pub fn show_prefs_dialog<T>(appstate: &Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
if appstate.gui
.prefs_dialog
.borrow()
@ -359,7 +361,9 @@ pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
/// Initialize the internal prefs dialog handler that connects to the audio
/// system.
pub fn init_prefs_callback(appstate: Rc<AppS>) {
pub fn init_prefs_callback<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if prefs window is not present */
@ -383,7 +387,9 @@ pub fn init_prefs_callback(appstate: Rc<AppS>) {
/// Initialize the preferences dialog gtk callbacks.
fn init_prefs_dialog(appstate: &Rc<AppS>) {
fn init_prefs_dialog<T>(appstate: &Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
/* prefs_dialog.connect_show */
{
@ -486,7 +492,9 @@ fn init_prefs_dialog(appstate: &Rc<AppS>) {
/// Fill the card combo box in the Devices tab.
fn fill_card_combo(appstate: &AppS) {
fn fill_card_combo<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let m_cc = appstate.gui.prefs_dialog.borrow();
let card_combo = &m_cc.as_ref().unwrap().card_combo;
card_combo.remove_all();
@ -513,7 +521,9 @@ fn fill_card_combo(appstate: &AppS) {
/// Fill the channel combo box in the Devices tab.
fn fill_chan_combo(appstate: &AppS, cardname: Option<String>) {
fn fill_chan_combo<T>(appstate: &AppS<T>, cardname: Option<String>)
where T: AudioFrontend
{
let m_cc = appstate.gui.prefs_dialog.borrow();
let chan_combo = &m_cc.as_ref().unwrap().chan_combo;
chan_combo.remove_all();
@ -543,10 +553,12 @@ fn fill_chan_combo(appstate: &AppS, cardname: Option<String>) {
}
fn on_hotkey_event_box_button_press_event(appstate: &AppS,
fn on_hotkey_event_box_button_press_event<T>(appstate: &AppS<T>,
widget: &gtk::EventBox,
event: &gdk::EventButton)
-> bool {
-> bool
where T: AudioFrontend
{
let borrow = appstate.gui.prefs_dialog.borrow();
let prefs_dialog = &borrow.as_ref().unwrap();
/* we want a left-click */

View File

@ -96,16 +96,14 @@ impl TrayIcon {
/// Update the tooltip of the tray icon.
fn update_tooltip(&self, audio: &Audio) {
let cardname = audio.acard
.borrow()
.card_name()
.unwrap_or(String::from("Unknown card"));
let channame = audio.acard
.borrow()
.chan_name()
.unwrap_or(String::from("unknown channel"));
let vol = audio.vol()
fn update_tooltip<T>(&self, audio: &T)
where T: AudioFrontend
{
let cardname =
audio.card_name().unwrap_or(String::from("Unknown card"));
let channame =
audio.chan_name().unwrap_or(String::from("unknown channel"));
let vol = audio.get_vol()
.map(|s| format!("{}", s.round()))
.unwrap_or(String::from("unknown volume"));
let mute_info = {
@ -127,11 +125,13 @@ impl TrayIcon {
/// Update the whole tray icon state.
pub fn update_all(&self,
pub fn update_all<T>(&self,
prefs: &Prefs,
audio: &Audio,
audio: &T,
m_size: Option<i32>)
-> Result<()> {
-> Result<()>
where T: AudioFrontend
{
match m_size {
Some(s) => {
if s < ICON_MIN_SIZE {
@ -153,8 +153,8 @@ impl TrayIcon {
*self.volmeter.borrow_mut() = Some(volmeter);
}
self.update_tooltip(&audio);
return self.update_vol_meter(audio.vol()?, audio.vol_level());
self.update_tooltip(audio);
return self.update_vol_meter(audio.get_vol()?, audio.vol_level());
}
}
@ -378,7 +378,9 @@ impl AudioPix {
/// Initialize the tray icon subsystem.
pub fn init_tray_icon(appstate: Rc<AppS>) {
pub fn init_tray_icon<T>(appstate: Rc<AppS<T>>)
where T: AudioFrontend + 'static
{
let tray_icon = &appstate.gui.tray_icon;
tray_icon.status_icon.set_visible(true);
@ -388,9 +390,9 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| match (s, u) {
(_, _) => {
apps.gui.tray_icon.update_tooltip(&apps.audio);
apps.gui.tray_icon.update_tooltip(apps.audio.as_ref());
try_w!(apps.gui.tray_icon.update_vol_meter(try_w!(apps.audio
.vol()),
.get_vol()),
apps.audio.vol_level()));
}
}));
@ -401,7 +403,7 @@ 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,
apps.audio.as_ref(),
Some(size)),
false);
return false;
@ -448,15 +450,17 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
));
default_theme.connect_changed(move |_| {
let tray_icon = &apps.gui.tray_icon;
let audio = &apps.audio;
try_e!(tray_icon.update_all(&apps.prefs.borrow_mut(), &audio, None));
try_e!(tray_icon.update_all(&apps.prefs.borrow_mut(),
apps.audio.as_ref(), None));
});
}
}
/// When the tray icon is activated.
fn on_tray_icon_activate(appstate: &AppS) {
fn on_tray_icon_activate<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let popup_window = &appstate.gui.popup_window.popup_window;
if popup_window.get_visible() {
@ -468,7 +472,9 @@ fn on_tray_icon_activate(appstate: &AppS) {
/// When the popup menu is shown, hide the popup window, if any.
fn on_tray_icon_popup_menu(appstate: &AppS) {
fn on_tray_icon_popup_menu<T>(appstate: &AppS<T>)
where T: AudioFrontend
{
let popup_window = &appstate.gui.popup_window.popup_window;
let popup_menu = &appstate.gui.popup_menu.menu;
@ -479,9 +485,11 @@ fn on_tray_icon_popup_menu(appstate: &AppS) {
/// When the mouse scroll event happens while the mouse pointer is
/// on the tray icon.
fn on_tray_icon_scroll_event(appstate: &AppS,
fn on_tray_icon_scroll_event<T>(appstate: &AppS<T>,
event: &gdk::EventScroll)
-> bool {
-> bool
where T: AudioFrontend
{
let scroll_dir: gdk::ScrollDirection = event.get_direction();
match scroll_dir {
@ -511,9 +519,11 @@ fn on_tray_icon_scroll_event(appstate: &AppS,
/// Basically when the tray icon is clicked (although we connect to the `release`
/// event). This decides whether it was a left, right or middle-click and
/// takes appropriate actions.
fn on_tray_button_release_event(appstate: &Rc<AppS>,
fn on_tray_button_release_event<T>(appstate: &Rc<AppS<T>>,
event_button: &gdk::EventButton)
-> bool {
-> bool
where T: AudioFrontend + 'static
{
let button = event_button.get_button();
if button != 2 {