This commit is contained in:
Julian Ospald 2017-07-10 21:07:59 +02:00
parent 3b943131df
commit 20e5b97842
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
11 changed files with 127 additions and 76 deletions

View File

@ -24,7 +24,7 @@ serde_derive = "^1.0.9"
toml = "^0.4.2"
which = "*"
xdg = "*"
libnotify = { path = "3rdparty/rust-libnotify" }
libnotify = { path = "3rdparty/rust-libnotify", optional = true }
[dependencies.gtk]
git = "https://github.com/gtk-rs/gtk.git"
@ -45,3 +45,6 @@ codegen-units = 1 # controls whether the compiler passes `-C codegen-units`
# `codegen-units` is ignored when `lto = true`
panic = 'unwind' # panic strategy (`-C panic=...`), can also be 'abort'
[features]
notify = ["libnotify"]

View File

@ -262,17 +262,17 @@ extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
glib_sys::G_IO_STATUS_AGAIN => {
debug!("G_IO_STATUS_AGAIN");
continue;
},
}
// TODO: handle these failure cases
glib_sys::G_IO_STATUS_NORMAL => {
error!("Alsa failed to clear the channel");
cb(AlsaEvent::AlsaCardError);
},
}
glib_sys::G_IO_STATUS_ERROR => (),
glib_sys::G_IO_STATUS_EOF => {
error!("GIO error has occurred");
cb(AlsaEvent::AlsaCardError);
},
}
_ => warn!("Unknown status from g_io_channel_read_chars()"),
}
return true as glib_sys::gboolean;

View File

@ -1,12 +1,12 @@
use audio::Audio;
use errors::*;
use gtk;
use notif;
use prefs::*;
use std::cell::RefCell;
use std::rc::Rc;
use ui_entry::Gui;
#[cfg(feature = "notify")]
use notif::*;
// TODO: notify popups
@ -19,6 +19,8 @@ pub struct AppS {
pub gui: Gui,
pub audio: Audio,
pub prefs: RefCell<Prefs>,
#[cfg(feature = "notify")]
pub notif: Notif,
}
@ -42,14 +44,17 @@ impl AppS {
.device_prefs
.channel
.clone();
// let notif = Notif::new(&prefs.borrow());
// TODO: better error handling
#[cfg(feature = "notify")]
let notif = Notif::new(&prefs.borrow()).unwrap();
return AppS {
_cant_construct: (),
gui,
audio: Audio::new(Some(card_name), Some(chan_name)).unwrap(),
prefs,
#[cfg(feature = "notify")]
notif,
};
}

View File

@ -141,8 +141,8 @@ impl Audio {
}
// invoke_handlers(&self.handlers.borrow(),
// AudioSignal::CardCleanedUp,
// user);
// AudioSignal::CardCleanedUp,
// user);
invoke_handlers(&self.handlers.borrow(),
AudioSignal::CardInitialized,
user);
@ -341,12 +341,12 @@ fn on_alsa_event(last_action_timestamp: &mut i64,
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,

View File

@ -1,10 +1,11 @@
use alsa;
use glib;
use libnotify;
use std::convert::From;
use std;
use toml;
#[cfg(feature = "notify")]
use libnotify;
error_chain! {
@ -12,12 +13,13 @@ error_chain! {
Alsa(alsa::Error);
IO(std::io::Error);
Toml(toml::de::Error);
Libnotify(libnotify::errors::Error);
Libnotify(libnotify::errors::Error) #[cfg(feature = "notify")];
}
}
#[macro_export]
macro_rules! try_w {
($expr:expr) => {
@ -64,7 +66,7 @@ macro_rules! try_wr {
macro_rules! try_r {
($expr:expr, $ret:expr) => (match $expr {
::std::result::Result::Ok(val) => val,
::std::result::Result::Err(err) => {
::std::result::Result::Err(_) => {
return $ret;
},
});

View File

@ -29,6 +29,8 @@ extern crate gtk_sys;
extern crate libc;
extern crate which;
extern crate xdg;
#[cfg(feature = "notify")]
extern crate libnotify;
use std::rc::Rc;
@ -52,6 +54,8 @@ mod ui_popup_menu;
mod ui_popup_window;
mod ui_prefs_dialog;
mod ui_tray_icon;
#[cfg(feature = "notify")]
mod notif;
@ -63,19 +67,10 @@ use app_state::*;
fn main() {
gtk::init().unwrap();
// TODO: error handling
#[cfg(feature = "notify")]
libnotify::init("PNMixer-rs").unwrap();
{
// Create a new notification and show it
let n = libnotify::Notification::new("Summary", Some("Optional Body"), None)
.unwrap();
n.show().unwrap();
// You can also use the .show() convenience method on the context
n.update("I am another notification", None, None).unwrap();
n.show().unwrap();
// we are done, deinit
}
flexi_logger::LogOptions::new()
.log_to_file(false)
// ... your configuration options go here ...
@ -87,5 +82,7 @@ fn main() {
ui_entry::init(apps);
gtk::main();
#[cfg(feature = "notify")]
libnotify::uninit();
}

View File

@ -30,7 +30,6 @@ pub struct Notif {
from_popup: Cell<bool>,
from_tray: Cell<bool>,
// TODO: from hotkey
from_external: Cell<bool>,
volume_notif: libnotify::Notification,
@ -58,18 +57,16 @@ impl Notif {
let timeout = prefs.notify_prefs.notifcation_timeout;
self.enabled.set(prefs.notify_prefs.enable_notifications);
self.enabled.set(prefs.notify_prefs.notify_popup);
self.enabled.set(prefs.notify_prefs.notify_mouse_scroll);
self.enabled.set(prefs.notify_prefs.notify_external);
self.from_popup.set(prefs.notify_prefs.notify_popup);
self.from_tray.set(prefs.notify_prefs.notify_mouse_scroll);
self.from_external.set(prefs.notify_prefs.notify_external);
self.volume_notif
.set_notification_timeout(timeout as i32);
self.volume_notif.set_notification_timeout(timeout as i32);
self.volume_notif
.set_hint("x-canonical-private-synchronous", Some("".to_variant()))?;
self.text_notif
.set_notification_timeout(timeout as i32);
self.text_notif.set_notification_timeout(timeout as i32);
self.text_notif
.set_hint("x-canonical-private-synchronous", Some("".to_variant()))?;
@ -95,17 +92,19 @@ impl Notif {
VolLevel::Muted => String::from("Volume muted"),
_ => {
format!("{} ({})\nVolume: {}",
audio.acard.borrow().card_name()?,
audio.acard.borrow().chan_name()?,
audio.acard
.borrow()
.card_name()?,
audio.acard
.borrow()
.chan_name()?,
vol)
}
}
};
self.volume_notif
.update(summary.as_str(), None, Some(icon))?;
self.volume_notif
.set_hint("value", Some(vol.to_variant()))?;
self.volume_notif.update(summary.as_str(), None, Some(icon))?;
self.volume_notif.set_hint("value", Some((vol as i32).to_variant()))?;
self.volume_notif.show()?;
return Ok(());
@ -113,8 +112,7 @@ impl Notif {
pub fn show_text_notif(&self, summary: &str, body: &str) -> Result<()> {
self.text_notif
.update(summary, Some(body), None)?;
self.text_notif.update(summary, Some(body), None)?;
self.text_notif.show()?;
return Ok(());
@ -124,25 +122,35 @@ impl Notif {
pub fn init_notify(appstate: Rc<AppS>) {
debug!("Blah");
{
/* connect handler */
let apps = appstate.clone();
let notif = try_e!(Notif::new(&apps.prefs.borrow()));
appstate.audio.connect_handler(
Box::new(move |s, u| match (s, u) {
(AudioSignal::CardDisconnected, _) => (),
(AudioSignal::CardError, _) => (),
(AudioSignal::ValuesChanged, AudioUser::TrayIcon) => {
try_w!(notif.show_volume_notif(&apps.audio))
},
appstate.audio.connect_handler(Box::new(move |s, u| {
let notif = &apps.notif;
if !notif.enabled.get() {
return;
}
match (s,
u,
(notif.from_popup.get(),
notif.from_tray.get(),
notif.from_external.get())) {
(AudioSignal::CardDisconnected, _, _) => (),
(AudioSignal::CardError, _, _) => (),
(AudioSignal::ValuesChanged,
AudioUser::TrayIcon,
(_, true, _)) => try_w!(notif.show_volume_notif(&apps.audio)),
(AudioSignal::ValuesChanged,
AudioUser::Popup,
(true, _, _)) => try_w!(notif.show_volume_notif(&apps.audio)),
(AudioSignal::ValuesChanged,
AudioUser::Unknown,
(_, _, true)) => try_w!(notif.show_volume_notif(&apps.audio)),
_ => (),
}
)
);
}
}));
}
}

View File

@ -140,6 +140,7 @@ impl Default for BehaviorPrefs {
}
#[cfg(feature = "notify")]
#[derive(Deserialize, Debug, Serialize)]
#[serde(default)]
pub struct NotifyPrefs {
@ -151,6 +152,7 @@ pub struct NotifyPrefs {
// TODO: notify_hotkeys?
}
#[cfg(feature = "notify")]
impl Default for NotifyPrefs {
fn default() -> NotifyPrefs {
return NotifyPrefs {
@ -170,6 +172,7 @@ pub struct Prefs {
pub device_prefs: DevicePrefs,
pub view_prefs: ViewPrefs,
pub behavior_prefs: BehaviorPrefs,
#[cfg(feature = "notify")]
pub notify_prefs: NotifyPrefs,
// TODO: HotKeys?
}

View File

@ -44,12 +44,13 @@ pub fn pixbuf_new_from_theme(icon_name: &str,
pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> {
ensure!(!filename.is_empty(), "Filename is empty");
let mut syspath = String::new();
let sysdir = option_env!("PIXMAPSDIR").map(|s|{
syspath = format!("{}/{}", s, filename);
Path::new(syspath.as_str())
});
let cargopath = format!("./data/pixmaps/{}",
filename);
let sysdir = option_env!("PIXMAPSDIR").map(|s| {
syspath = format!("{}/{}",
s,
filename);
Path::new(syspath.as_str())
});
let cargopath = format!("./data/pixmaps/{}", filename);
let cargodir = Path::new(cargopath.as_str());
// prefer local dir

View File

@ -5,7 +5,7 @@ use gtk::MessageDialogExt;
use gtk::WidgetExt;
use gtk::WindowExt;
use gtk;
use gtk_sys::{GTK_DIALOG_DESTROY_WITH_PARENT, GTK_RESPONSE_YES};
use gtk_sys::{GTK_RESPONSE_YES};
use prefs::*;
use std::cell::RefCell;
use std::rc::Rc;
@ -14,8 +14,9 @@ use ui_popup_menu::*;
use ui_popup_window::*;
use ui_prefs_dialog::*;
use ui_tray_icon::*;
use notif::*;
#[cfg(feature = "notify")]
use notif::*;
@ -26,6 +27,7 @@ pub struct Gui {
pub popup_menu: PopupMenu,
/* prefs_dialog is dynamically created and destroyed */
pub prefs_dialog: RefCell<Option<PrefsDialog>>,
}
impl Gui {
@ -72,6 +74,8 @@ pub fn init(appstate: Rc<AppS>) {
init_popup_window(appstate.clone());
init_popup_menu(appstate.clone());
init_prefs_callback(appstate.clone());
#[cfg(feature = "notify")]
init_notify(appstate.clone());
}
@ -79,13 +83,11 @@ pub fn init(appstate: Rc<AppS>) {
fn run_audio_error_dialog(parent: &gtk::Window) -> i32 {
error!("Connection with audio failed, you probably need to restart pnmixer.");
let dialog = gtk::MessageDialog::new(
Some(parent),
gtk::DIALOG_DESTROY_WITH_PARENT,
gtk::MessageType::Error,
gtk::ButtonsType::YesNo,
"Warning: Connection to sound system failed."
);
let dialog = gtk::MessageDialog::new(Some(parent),
gtk::DIALOG_DESTROY_WITH_PARENT,
gtk::MessageType::Error,
gtk::ButtonsType::YesNo,
"Warning: Connection to sound system failed.");
dialog.set_property_secondary_text(Some("Do you want to re-initialize the audio connection ?
If you do not, you will either need to restart PNMixer

View File

@ -19,6 +19,7 @@ use support_audio::*;
pub struct PrefsDialog {
_cant_construct: (),
prefs_dialog: gtk::Dialog,
notebook: gtk::Notebook,
/* DevicePrefs */
card_combo: gtk::ComboBoxText,
@ -38,11 +39,16 @@ pub struct PrefsDialog {
custom_entry: gtk::Entry,
/* NotifyPrefs */
#[cfg(feature = "notify")]
noti_enable_check: gtk::CheckButton,
#[cfg(feature = "notify")]
noti_timeout_spin: gtk::SpinButton,
// pub noti_hotkey_check: gtk::CheckButton,
#[cfg(feature = "notify")]
noti_mouse_check: gtk::CheckButton,
#[cfg(feature = "notify")]
noti_popup_check: gtk::CheckButton,
#[cfg(feature = "notify")]
noti_ext_check: gtk::CheckButton,
}
@ -54,6 +60,7 @@ impl PrefsDialog {
let prefs_dialog = PrefsDialog {
_cant_construct: (),
prefs_dialog: builder.get_object("prefs_dialog").unwrap(),
notebook: builder.get_object("notebook").unwrap(),
card_combo: builder.get_object("card_combo").unwrap(),
chan_combo: builder.get_object("chan_combo").unwrap(),
@ -74,15 +81,26 @@ impl PrefsDialog {
.unwrap(),
custom_entry: builder.get_object("custom_entry").unwrap(),
#[cfg(feature = "notify")]
noti_enable_check: builder.get_object("noti_enable_check").unwrap(),
#[cfg(feature = "notify")]
noti_timeout_spin: builder.get_object("noti_timeout_spin").unwrap(),
// noti_hotkey_check: builder.get_object("noti_hotkey_check").unwrap(),
#[cfg(feature = "notify")]
noti_mouse_check: builder.get_object("noti_mouse_check").unwrap(),
#[cfg(feature = "notify")]
noti_popup_check: builder.get_object("noti_popup_check").unwrap(),
#[cfg(feature = "notify")]
noti_ext_check: builder.get_object("noti_ext_check").unwrap(),
};
#[cfg(feature = "notify")]
let notify_tab: gtk::Box = builder.get_object("noti_vbox_enabled").unwrap();
#[cfg(not(feature = "notify"))]
let notify_tab: gtk::Box = builder.get_object("noti_vbox_disabled").unwrap();
prefs_dialog.notebook.append_page(&notify_tab,
Some(&gtk::Label::new(Some("Notifications"))));
return prefs_dialog;
}
@ -134,13 +152,18 @@ impl PrefsDialog {
.as_str());
/* NotifyPrefs */
#[cfg(feature = "notify")]
self.noti_enable_check
.set_active(prefs.notify_prefs.enable_notifications);
#[cfg(feature = "notify")]
self.noti_timeout_spin
.set_value(prefs.notify_prefs.notifcation_timeout as f64);
#[cfg(feature = "notify")]
self.noti_mouse_check
.set_active(prefs.notify_prefs.notify_mouse_scroll);
#[cfg(feature = "notify")]
self.noti_popup_check.set_active(prefs.notify_prefs.notify_popup);
#[cfg(feature = "notify")]
self.noti_ext_check.set_active(prefs.notify_prefs.notify_external);
}
@ -193,6 +216,7 @@ impl PrefsDialog {
custom_command,
};
#[cfg(feature = "notify")]
let notify_prefs = NotifyPrefs {
enable_notifications: self.noti_enable_check.get_active(),
notifcation_timeout: self.noti_timeout_spin.get_value_as_int() as
@ -206,6 +230,7 @@ impl PrefsDialog {
device_prefs,
view_prefs,
behavior_prefs,
#[cfg(feature = "notify")]
notify_prefs,
};
@ -240,7 +265,10 @@ pub fn init_prefs_callback(appstate: Rc<AppS>) {
let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| {
/* skip if prefs window is not present */
if apps.gui.prefs_dialog.borrow().is_none() {
if apps.gui
.prefs_dialog
.borrow()
.is_none() {
return;
}
@ -249,7 +277,7 @@ pub fn init_prefs_callback(appstate: Rc<AppS>) {
(AudioSignal::CardCleanedUp, _) => {
fill_card_combo(&apps);
fill_chan_combo(&apps, None);
},
}
_ => (),
}
}));
@ -308,6 +336,8 @@ fn init_prefs_dialog(appstate: &Rc<AppS>) {
if response_id == ResponseType::Ok.into() ||
response_id == ResponseType::Apply.into() {
// TODO: update popup, tray_icon, hotkeys, notification and audio
#[cfg(feature = "notify")]
try_w!(apps.notif.reload(&apps.prefs.borrow()));
try_w!(apps.update_tray_icon());
try_w!(apps.update_popup_window());
{