Update
This commit is contained in:
parent
3b943131df
commit
20e5b97842
@ -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"]
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
},
|
||||
});
|
||||
|
19
src/main.rs
19
src/main.rs
@ -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();
|
||||
}
|
||||
|
70
src/notif.rs
70
src/notif.rs
@ -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)),
|
||||
|
||||
_ => (),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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?
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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: >k::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
|
||||
|
@ -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(¬ify_tab,
|
||||
Some(>k::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());
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user