This commit is contained in:
Julian Ospald 2017-07-09 00:14:49 +02:00
parent 5e0d7f9550
commit 45cdc5f9ca
No known key found for this signature in database
GPG Key ID: 511B62C09D50CD28
18 changed files with 288 additions and 93 deletions

View File

@ -6,23 +6,24 @@ authors = ["Julian Ospald <hasufell@posteo.de>"]
[dependencies] [dependencies]
alsa = "^0.1.8" alsa = "^0.1.8"
alsa-sys = "^0.1.1" alsa-sys = "^0.1.1"
libc = "^0.2.23" error-chain = { path = "3rdparty/error-chain" }
gdk-sys = { git = "https://github.com/gtk-rs/sys" } ffi = "^0.0.2"
flexi_logger = "^0.5.1"
gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" } gdk-pixbuf = { git = "https://github.com/gtk-rs/gdk-pixbuf.git" }
gdk-pixbuf-sys = { git = "https://github.com/gtk-rs/sys" } gdk-pixbuf-sys = { git = "https://github.com/gtk-rs/sys" }
gtk-sys = { git = "https://github.com/gtk-rs/sys" } gdk-sys = { git = "https://github.com/gtk-rs/sys" }
gio = { git = "https://github.com/gtk-rs/gio.git" }
glib = { git = "https://github.com/gtk-rs/glib.git" } glib = { git = "https://github.com/gtk-rs/glib.git" }
glib-sys = { git = "https://github.com/gtk-rs/sys" } glib-sys = { git = "https://github.com/gtk-rs/sys" }
gobject-sys = { git = "https://github.com/gtk-rs/sys" } gobject-sys = { git = "https://github.com/gtk-rs/sys" }
ffi = "^0.0.2" gtk-sys = { git = "https://github.com/gtk-rs/sys" }
flexi_logger = "^0.5.1" libc = "^0.2.23"
log = "^0.3.8" log = "^0.3.8"
error-chain = { path = "3rdparty/error-chain" }
toml = "^0.4.2"
serde_derive = "^1.0.9"
serde = "^1.0.9" serde = "^1.0.9"
xdg = "*" serde_derive = "^1.0.9"
toml = "^0.4.2"
which = "*" which = "*"
xdg = "*"
[dependencies.gtk] [dependencies.gtk]
git = "https://github.com/gtk-rs/gtk.git" git = "https://github.com/gtk-rs/gtk.git"
@ -33,12 +34,12 @@ git = "https://github.com/gtk-rs/gdk.git"
features = [ "v3_10", "v3_12", "v3_22" ] features = [ "v3_10", "v3_12", "v3_22" ]
[profile.dev] [profile.dev]
opt-level = 2 # controls the `--opt-level` the compiler builds with opt-level = 0 # controls the `--opt-level` the compiler builds with
debug = true # controls whether the compiler passes `-C debuginfo` debug = true # controls whether the compiler passes `-C debuginfo`
# a value of `true` is equivalent to `2` # a value of `true` is equivalent to `2`
rpath = false # controls whether the compiler passes `-C rpath` rpath = false # controls whether the compiler passes `-C rpath`
lto = false # controls `-C lto` for binaries and staticlibs lto = false # controls `-C lto` for binaries and staticlibs
debug-assertions = true # controls whether debug assertions are enabled debug-assertions = false # controls whether debug assertions are enabled
codegen-units = 1 # controls whether the compiler passes `-C codegen-units` codegen-units = 1 # controls whether the compiler passes `-C codegen-units`
# `codegen-units` is ignored when `lto = true` # `codegen-units` is ignored when `lto = true`
panic = 'unwind' # panic strategy (`-C panic=...`), can also be 'abort' panic = 'unwind' # panic strategy (`-C panic=...`), can also be 'abort'

58
Makefile Normal file
View File

@ -0,0 +1,58 @@
INSTALL = install
INSTALL_DIR = $(INSTALL) -d
INSTALL_BIN = $(INSTALL) -m 755
INSTALL_DATA = $(INSTALL) -m 644
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
SHAREDIR=$(PREFIX)/share
DATADIR=$(SHAREDIR)/pnmixer
PIXMAPSDIR=$(DATADIR)/pixmaps
ICONSDIR=$(SHAREDIR)/icons/hicolor/128x128/apps
DESKTOPDIR=$(SHAREDIR)/applications
CARGO ?= cargo
CARGO_ARGS ?=
CARGO_BUILD_ARGS ?= --release
CARGO_BUILD ?= $(CARGO) $(CARGO_ARGS) build $(CARGO_BUILD_ARGS)
CARGO_INSTALL_ARGS ?= --root="$(DESTDIR)/$(PREFIX)"
CARGO_INSTALL ?= $(CARGO) $(CARGO_ARGS) install $(CARGO_INSTALL_ARGS)
pnmixer-rs: Cargo.toml
PIXMAPSDIR=$(PIXMAPSDIR) $(CARGO_BUILD)
install: install-data
$(INSTALL_DIR) "$(DESTDIR)/$(BINDIR)"
$(INSTALL_BIN) target/release/pnmixer "$(DESTDIR)/$(BINDIR)/pnmixer"
install-data: install-pixmaps install-icons install-desktop
install-pixmaps:
$(INSTALL_DIR) "$(DESTDIR)/$(PIXMAPSDIR)"
$(INSTALL_DATA) data/pixmaps/pnmixer-about.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-about.png"
$(INSTALL_DATA) data/pixmaps/pnmixer-high.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-high.png"
$(INSTALL_DATA) data/pixmaps/pnmixer-low.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-low.png"
$(INSTALL_DATA) data/pixmaps/pnmixer-medium.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-medium.png"
$(INSTALL_DATA) data/pixmaps/pnmixer-muted.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-muted.png"
$(INSTALL_DATA) data/pixmaps/pnmixer-off.png "$(DESTDIR)/$(PIXMAPSDIR)/pnmixer-off.png"
install-icons:
$(INSTALL_DIR) "$(DESTDIR)/$(ICONSDIR)"
$(INSTALL_DATA) data/icons/pnmixer.png "$(DESTDIR)/$(ICONSDIR)/pnmixer.png"
install-desktop:
$(INSTALL_DIR) "$(DESTDIR)/$(DESKTOPDIR)"
$(INSTALL_DATA) data/desktop/pnmixer.desktop "$(DESTDIR)/$(DESKTOPDIR)/pnmixer.desktop"
.PHONY: pnmixer-rs install install-data install-pixmaps install-icons install-desktop

View File

@ -1,5 +1,5 @@
[Desktop Entry] [Desktop Entry]
Name=PNMixer Name=PNMixer-rs
_GenericName=System Tray Mixer _GenericName=System Tray Mixer
_Comment=An audio mixer for the system tray _Comment=An audio mixer for the system tray
Exec=pnmixer Exec=pnmixer

View File

@ -12,8 +12,6 @@
<property name="destroy_with_parent">True</property> <property name="destroy_with_parent">True</property>
<property name="icon_name">input-keyboard</property> <property name="icon_name">input-keyboard</property>
<property name="type_hint">dialog</property> <property name="type_hint">dialog</property>
<signal name="key-press-event" handler="on_hotkey_dialog_key_press_event" swapped="no"/>
<signal name="key-release-event" handler="on_hotkey_dialog_key_release_event" swapped="no"/>
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox1"> <object class="GtkBox" id="dialog-vbox1">
<property name="visible">True</property> <property name="visible">True</property>

View File

@ -31,7 +31,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Mute/Unmute Volume</property> <property name="tooltip_text" translatable="yes">Mute/Unmute Volume</property>
<signal name="activate" handler="on_mute_item_activate" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box0"> <object class="GtkBox" id="box0">
<property name="visible">True</property> <property name="visible">True</property>
@ -71,7 +70,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open Volume Control</property> <property name="tooltip_text" translatable="yes">Open Volume Control</property>
<signal name="activate" handler="on_mixer_item_activate" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box1"> <object class="GtkBox" id="box1">
<property name="visible">True</property> <property name="visible">True</property>
@ -108,7 +106,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Preferences</property> <property name="tooltip_text" translatable="yes">Preferences</property>
<signal name="activate" handler="on_prefs_item_activate" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box2"> <object class="GtkBox" id="box2">
<property name="visible">True</property> <property name="visible">True</property>
@ -146,7 +143,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Reload Sound</property> <property name="tooltip_text" translatable="yes">Reload Sound</property>
<signal name="activate" handler="on_reload_item_activate" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box3"> <object class="GtkBox" id="box3">
<property name="visible">True</property> <property name="visible">True</property>
@ -184,7 +180,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">About</property> <property name="tooltip_text" translatable="yes">About</property>
<signal name="activate" handler="on_about_item_activate" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box4"> <object class="GtkBox" id="box4">
<property name="visible">True</property> <property name="visible">True</property>
@ -229,7 +224,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Quit</property> <property name="tooltip_text" translatable="yes">Quit</property>
<signal name="activate" handler="gtk_main_quit" swapped="no"/>
<child> <child>
<object class="GtkBox" id="box5"> <object class="GtkBox" id="box5">
<property name="visible">True</property> <property name="visible">True</property>

View File

@ -16,9 +16,6 @@
<property name="skip_taskbar_hint">True</property> <property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property> <property name="skip_pager_hint">True</property>
<property name="decorated">False</property> <property name="decorated">False</property>
<signal name="button-press-event" handler="on_popup_window_event" swapped="no"/>
<signal name="grab-broken-event" handler="on_popup_window_event" swapped="no"/>
<signal name="key-press-event" handler="on_popup_window_event" swapped="no"/>
<child> <child>
<object class="GtkBox" id="vbox1"> <object class="GtkBox" id="vbox1">
<property name="visible">True</property> <property name="visible">True</property>
@ -36,7 +33,6 @@
<property name="round_digits">0</property> <property name="round_digits">0</property>
<property name="digits">0</property> <property name="digits">0</property>
<property name="height_request">200</property> <property name="height_request">200</property>
<signal name="value-changed" handler="on_vol_scale_value_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@ -58,7 +54,6 @@
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_mute_check_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -73,7 +68,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<signal name="clicked" handler="on_mixer_button_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View File

@ -69,7 +69,6 @@
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_noti_enable_check_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -233,7 +232,7 @@
</object> </object>
<object class="GtkDialog" id="prefs_dialog"> <object class="GtkDialog" id="prefs_dialog">
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="title" translatable="yes">PNMixer Preferences</property> <property name="title" translatable="yes">PNMixer-rs Preferences</property>
<property name="resizable">False</property> <property name="resizable">False</property>
<property name="icon_name">preferences-system</property> <property name="icon_name">preferences-system</property>
<property name="type_hint">dialog</property> <property name="type_hint">dialog</property>
@ -447,7 +446,6 @@
<object class="GtkComboBoxText" id="card_combo"> <object class="GtkComboBoxText" id="card_combo">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<signal name="changed" handler="on_card_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -581,7 +579,6 @@
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_vol_meter_draw_check_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
@ -744,6 +741,35 @@
<property name="top_attach">0</property> <property name="top_attach">0</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="label32">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Fine Scroll Step:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="fine_scroll_step_spin">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="text" translatable="yes">0,00</property>
<property name="primary_icon_activatable">False</property>
<property name="secondary_icon_activatable">False</property>
<property name="adjustment">fine_scroll_step_adjustment</property>
<property name="digits">2</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object> </object>
</child> </child>
<child type="label"> <child type="label">
@ -805,7 +831,6 @@
<object class="GtkComboBoxText" id="middle_click_combo"> <object class="GtkComboBoxText" id="middle_click_combo">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<signal name="changed" handler="on_middle_click_combo_changed" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
@ -891,7 +916,6 @@
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
<signal name="toggled" handler="on_hotkeys_enable_check_toggled" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@ -976,7 +1000,6 @@
<object class="GtkEventBox" id="hotkeys_mute_eventbox"> <object class="GtkEventBox" id="hotkeys_mute_eventbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<signal name="button-press-event" handler="on_hotkey_event_box_button_press_event" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="hotkeys_mute_label"> <object class="GtkLabel" id="hotkeys_mute_label">
<property name="visible">True</property> <property name="visible">True</property>
@ -997,7 +1020,6 @@
<object class="GtkEventBox" id="hotkeys_up_eventbox"> <object class="GtkEventBox" id="hotkeys_up_eventbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<signal name="button-press-event" handler="on_hotkey_event_box_button_press_event" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="hotkeys_up_label"> <object class="GtkLabel" id="hotkeys_up_label">
<property name="visible">True</property> <property name="visible">True</property>
@ -1018,7 +1040,6 @@
<object class="GtkEventBox" id="hotkeys_down_eventbox"> <object class="GtkEventBox" id="hotkeys_down_eventbox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<signal name="button-press-event" handler="on_hotkey_event_box_button_press_event" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="hotkeys_down_label"> <object class="GtkLabel" id="hotkeys_down_label">
<property name="visible">True</property> <property name="visible">True</property>

View File

@ -233,6 +233,7 @@ extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
let acard = let acard =
unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) }; unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) };
let cb = &acard.cb;
unsafe { unsafe {
let mixer_ptr = let mixer_ptr =
@ -261,15 +262,21 @@ extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
glib_sys::G_IO_STATUS_AGAIN => { glib_sys::G_IO_STATUS_AGAIN => {
debug!("G_IO_STATUS_AGAIN"); debug!("G_IO_STATUS_AGAIN");
continue; continue;
} },
// TODO: handle these failure cases // TODO: handle these failure cases
glib_sys::G_IO_STATUS_NORMAL => debug!("G_IO_STATUS_NORMAL"), glib_sys::G_IO_STATUS_NORMAL => {
glib_sys::G_IO_STATUS_ERROR => debug!("G_IO_STATUS_ERROR"), error!("Alsa failed to clear the channel");
glib_sys::G_IO_STATUS_EOF => debug!("G_IO_STATUS_EOF"), 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; return true as glib_sys::gboolean;
} }
let cb = &acard.cb;
cb(AlsaEvent::AlsaCardValuesChanged); cb(AlsaEvent::AlsaCardValuesChanged);
return true as glib_sys::gboolean; return true as glib_sys::gboolean;

View File

@ -5,6 +5,7 @@ use prefs::*;
use std::cell::RefCell; use std::cell::RefCell;
use ui_entry::Gui; use ui_entry::Gui;
use ui_prefs_dialog::show_prefs_dialog; use ui_prefs_dialog::show_prefs_dialog;
use notif::*;
// TODO: notify popups // TODO: notify popups
@ -17,6 +18,7 @@ pub struct AppS {
_cant_construct: (), _cant_construct: (),
pub gui: Gui, pub gui: Gui,
pub audio: Audio, pub audio: Audio,
pub notif: Notif,
pub prefs: RefCell<Prefs>, pub prefs: RefCell<Prefs>,
} }
@ -42,11 +44,14 @@ impl AppS {
.channel .channel
.clone(); .clone();
let notif = Notif::new(&prefs.borrow());
return AppS { return AppS {
_cant_construct: (), _cant_construct: (),
gui: gui, gui,
audio: Audio::new(Some(card_name), Some(chan_name)).unwrap(), audio: Audio::new(Some(card_name), Some(chan_name)).unwrap(),
prefs: prefs, notif,
prefs,
}; };
} }
@ -64,9 +69,4 @@ impl AppS {
debug!("Update PopupWindow!"); debug!("Update PopupWindow!");
return self.gui.popup_window.update(&self.audio); return self.gui.popup_window.update(&self.audio);
} }
// TODO
pub fn show_preferences(&self) {
// show_prefs_dialog(self);
}
} }

View File

@ -90,9 +90,22 @@ impl Audio {
}) })
}; };
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 { let audio = Audio {
_cannot_construct: (), _cannot_construct: (),
acard: RefCell::new(AlsaCard::new(card_name, elem_name, cb)?), acard: RefCell::new(acard?),
last_action_timestamp: last_action_timestamp.clone(), last_action_timestamp: last_action_timestamp.clone(),
handlers: handlers.clone(), handlers: handlers.clone(),
scroll_step: Cell::new(5), scroll_step: Cell::new(5),
@ -126,17 +139,10 @@ impl Audio {
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!("Old card name: {}",
self.acard
.borrow()
.card_name()
.unwrap());
debug!("Old chan name: {}",
self.acard
.borrow()
.chan_name()
.unwrap());
// invoke_handlers(&self.handlers.borrow(),
// AudioSignal::CardCleanedUp,
// user);
invoke_handlers(&self.handlers.borrow(), invoke_handlers(&self.handlers.borrow(),
AudioSignal::CardInitialized, AudioSignal::CardInitialized,
user); user);
@ -216,9 +222,6 @@ impl Audio {
self.set_vol(new_vol, user)?; self.set_vol(new_vol, user)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(()); return Ok(());
} }
@ -245,9 +248,6 @@ impl Audio {
self.set_vol(new_vol, user)?; self.set_vol(new_vol, user)?;
invoke_handlers(&self.handlers.borrow(),
AudioSignal::ValuesChanged,
user);
return Ok(()); return Ok(());
} }
@ -307,6 +307,11 @@ fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
debug!("Invoking handlers for signal {:?} by user {:?}", debug!("Invoking handlers for signal {:?} by user {:?}",
signal, signal,
user); user);
if handlers.is_empty() {
debug!("No handler found");
} else {
debug!("Executing handlers")
}
for handler in handlers { for handler in handlers {
let unboxed = handler.as_ref(); let unboxed = handler.as_ref();
unboxed(signal, user); unboxed(signal, user);
@ -332,10 +337,17 @@ fn on_alsa_event(last_action_timestamp: &mut i64,
/* external change */ /* external change */
match alsa_event { match alsa_event {
// TODO: invoke handlers with AudioUserUnknown // TODO: invoke handlers with AudioUserUnknown
AlsaEvent::AlsaCardError => debug!("AlsaCardError"), AlsaEvent::AlsaCardError => {
AlsaEvent::AlsaCardDiconnected => debug!("AlsaCardDiconnected"), invoke_handlers(handlers,
self::AudioSignal::CardError,
self::AudioUser::Unknown);
},
AlsaEvent::AlsaCardDiconnected => {
invoke_handlers(handlers,
self::AudioSignal::CardDisconnected,
self::AudioUser::Unknown);
},
AlsaEvent::AlsaCardValuesChanged => { AlsaEvent::AlsaCardValuesChanged => {
debug!("AlsaCardValuesChanged");
invoke_handlers(handlers, invoke_handlers(handlers,
self::AudioSignal::ValuesChanged, self::AudioSignal::ValuesChanged,
self::AudioUser::Unknown); self::AudioUser::Unknown);

View File

@ -20,6 +20,7 @@ extern crate gdk;
extern crate gdk_pixbuf; extern crate gdk_pixbuf;
extern crate gdk_pixbuf_sys; extern crate gdk_pixbuf_sys;
extern crate gdk_sys; extern crate gdk_sys;
extern crate gio;
extern crate glib; extern crate glib;
extern crate glib_sys; extern crate glib_sys;
extern crate gobject_sys; extern crate gobject_sys;

View File

@ -122,6 +122,7 @@ impl Default for VolColor {
pub struct BehaviorPrefs { pub struct BehaviorPrefs {
pub vol_control_cmd: Option<String>, pub vol_control_cmd: Option<String>,
pub vol_scroll_step: f64, pub vol_scroll_step: f64,
pub vol_fine_scroll_step: f64,
pub middle_click_action: MiddleClickAction, pub middle_click_action: MiddleClickAction,
pub custom_command: Option<String>, // TODO: fine scroll step? pub custom_command: Option<String>, // TODO: fine scroll step?
} }
@ -131,6 +132,7 @@ impl Default for BehaviorPrefs {
return BehaviorPrefs { return BehaviorPrefs {
vol_control_cmd: None, vol_control_cmd: None,
vol_scroll_step: 5.0, vol_scroll_step: 5.0,
vol_fine_scroll_step: 1.0,
middle_click_action: MiddleClickAction::default(), middle_click_action: MiddleClickAction::default(),
custom_command: None, custom_command: None,
}; };

View File

@ -43,16 +43,28 @@ pub fn pixbuf_new_from_theme(icon_name: &str,
pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> { pub fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> {
ensure!(!filename.is_empty(), "Filename is empty"); 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 cargodir = Path::new(cargopath.as_str());
let s = format!("{}/data/pixmaps/{}", env!("CARGO_MANIFEST_DIR"), filename); // prefer local dir
let path = Path::new(s.as_str()); let final_dir = {
if cargodir.exists() {
cargodir
} else if sysdir.is_some() && sysdir.unwrap().exists() {
sysdir.unwrap()
} else {
bail!("No valid path found")
}
};
if path.exists() { let str_path = final_dir.to_str().ok_or("Path is not valid unicode")?;
let str_path = path.to_str().ok_or("Path is not valid unicode")?; debug!("Loading icon from {}", str_path);
// TODO: propagate error
// TODO: propagate error return Ok(gdk_pixbuf::Pixbuf::new_from_file(str_path).unwrap());
return Ok(gdk_pixbuf::Pixbuf::new_from_file(str_path).unwrap());
} else {
bail!("Uh-oh");
}
} }

View File

@ -1,13 +1,20 @@
use app_state::*; use app_state::*;
use audio::{AudioUser, AudioSignal};
use gtk::DialogExt;
use gtk::MessageDialogExt;
use gtk::WidgetExt;
use gtk::WindowExt;
use gtk; use gtk;
use gtk_sys::{GTK_DIALOG_DESTROY_WITH_PARENT, GTK_RESPONSE_YES};
use notif::*;
use prefs::*; use prefs::*;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use support_audio::*;
use ui_popup_menu::*; use ui_popup_menu::*;
use ui_popup_window::*; use ui_popup_window::*;
use ui_prefs_dialog::*; use ui_prefs_dialog::*;
use ui_tray_icon::*; use ui_tray_icon::*;
use audio::{AudioUser, AudioSignal};
@ -39,16 +46,25 @@ impl Gui {
pub fn init(appstate: Rc<AppS>) { pub fn init(appstate: Rc<AppS>) {
{ {
/* "global" audio signal handler */
let apps = appstate.clone(); let apps = appstate.clone();
appstate.audio.connect_handler( appstate.audio.connect_handler(
Box::new(move |s, u| match (s, u) { Box::new(move |s, u| match (s, u) {
(AudioSignal::ValuesChanged, AudioUser::Unknown) => { (AudioSignal::CardDisconnected, _) => {
debug!("Appstate references: {}!", Rc::strong_count(&apps)); try_w!(audio_reload(&apps.audio,
&apps.prefs.borrow(),
} AudioUser::Unknown));
_ => debug!("Nix"), },
}), (AudioSignal::CardError, _) => {
); if run_audio_error_dialog(&apps.gui.popup_menu.menu_window) == (GTK_RESPONSE_YES as i32) {
try_w!(audio_reload(&apps.audio,
&apps.prefs.borrow(),
AudioUser::Unknown));
}
},
_ => (),
}
));
} }
@ -57,3 +73,28 @@ pub fn init(appstate: Rc<AppS>) {
init_popup_menu(appstate.clone()); init_popup_menu(appstate.clone());
init_prefs_callback(appstate.clone()); init_prefs_callback(appstate.clone());
} }
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."
);
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
or select the 'Reload Audio' option in the right-click
menu in order for PNMixer to function."));
dialog.set_title("PNMixer-rs Error");
let resp = dialog.run();
dialog.destroy();
return resp;
}

View File

@ -127,7 +127,7 @@ fn create_about_dialog() -> gtk::AboutDialog {
let about_dialog: gtk::AboutDialog = gtk::AboutDialog::new(); let about_dialog: gtk::AboutDialog = gtk::AboutDialog::new();
about_dialog.set_license(Some( about_dialog.set_license(Some(
"PNMixer is free software; you can redistribute it and/or modify it "PNMixer-rs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License v3 as published under the terms of the GNU General Public License v3 as published
by the Free Software Foundation. by the Free Software Foundation.
@ -143,7 +143,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.",
about_dialog.set_copyright(Some("Copyright © 2017 Julian Ospald")); about_dialog.set_copyright(Some("Copyright © 2017 Julian Ospald"));
about_dialog.set_authors(&["Julian Ospald"]); about_dialog.set_authors(&["Julian Ospald"]);
about_dialog.set_artists(&["Paul Davey"]); about_dialog.set_artists(&["Paul Davey"]);
about_dialog.set_program_name("pnmixer-rs"); about_dialog.set_program_name("PNMixer-rs");
about_dialog.set_logo_icon_name("pnmixer"); about_dialog.set_logo_icon_name("pnmixer");
about_dialog.set_version(VERSION); about_dialog.set_version(VERSION);
about_dialog.set_website("https://github.com/hasufell/pnmixer-rust"); about_dialog.set_website("https://github.com/hasufell/pnmixer-rust");

View File

@ -9,6 +9,7 @@ use glib;
use gtk::ToggleButtonExt; use gtk::ToggleButtonExt;
use gtk::prelude::*; use gtk::prelude::*;
use gtk; use gtk;
use prefs::*;
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use support_cmd::*; use support_cmd::*;
@ -71,6 +72,13 @@ impl PopupWindow {
glib::signal_handler_unblock(&self.mute_check, glib::signal_handler_unblock(&self.mute_check,
self.toggle_signal.get()); self.toggle_signal.get());
} }
fn set_vol_increment(&self, prefs: &Prefs) {
self.vol_scale_adj
.set_page_increment(prefs.behavior_prefs.vol_scroll_step);
self.vol_scale_adj
.set_step_increment(prefs.behavior_prefs.vol_fine_scroll_step);
}
} }
@ -104,7 +112,6 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
} }
} }
})); }));
} }
/* mute_check.connect_toggled */ /* mute_check.connect_toggled */
@ -174,6 +181,7 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
fn on_popup_window_show(appstate: &AppS) { fn on_popup_window_show(appstate: &AppS) {
appstate.gui.popup_window.set_vol_increment(&appstate.prefs.borrow());
try_w!(appstate.gui.popup_window.update(&appstate.audio)); try_w!(appstate.gui.popup_window.update(&appstate.audio));
appstate.gui appstate.gui
.popup_window .popup_window

View File

@ -6,7 +6,6 @@ use gtk::ResponseType;
use gtk::prelude::*; use gtk::prelude::*;
use gtk; use gtk;
use prefs::*; use prefs::*;
use std::mem;
use std::rc::Rc; use std::rc::Rc;
use support_alsa::*; use support_alsa::*;
use support_audio::*; use support_audio::*;
@ -34,6 +33,7 @@ pub struct PrefsDialog {
/* BehaviorPrefs */ /* BehaviorPrefs */
vol_control_entry: gtk::Entry, vol_control_entry: gtk::Entry,
scroll_step_spin: gtk::SpinButton, scroll_step_spin: gtk::SpinButton,
fine_scroll_step_spin: gtk::SpinButton,
middle_click_combo: gtk::ComboBoxText, middle_click_combo: gtk::ComboBoxText,
custom_entry: gtk::Entry, custom_entry: gtk::Entry,
@ -68,6 +68,8 @@ impl PrefsDialog {
vol_control_entry: builder.get_object("vol_control_entry").unwrap(), vol_control_entry: builder.get_object("vol_control_entry").unwrap(),
scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(), scroll_step_spin: builder.get_object("scroll_step_spin").unwrap(),
fine_scroll_step_spin: builder.get_object("fine_scroll_step_spin")
.unwrap(),
middle_click_combo: builder.get_object("middle_click_combo") middle_click_combo: builder.get_object("middle_click_combo")
.unwrap(), .unwrap(),
custom_entry: builder.get_object("custom_entry").unwrap(), custom_entry: builder.get_object("custom_entry").unwrap(),
@ -113,6 +115,8 @@ impl PrefsDialog {
.unwrap_or(&String::from("")) .unwrap_or(&String::from(""))
.as_str()); .as_str());
self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step); self.scroll_step_spin.set_value(prefs.behavior_prefs.vol_scroll_step);
self.fine_scroll_step_spin
.set_value(prefs.behavior_prefs.vol_fine_scroll_step);
// TODO: make sure these values always match, must be a better way // TODO: make sure these values always match, must be a better way
// also check to_prefs() // also check to_prefs()
@ -184,6 +188,7 @@ impl PrefsDialog {
let behavior_prefs = BehaviorPrefs { let behavior_prefs = BehaviorPrefs {
vol_control_cmd, vol_control_cmd,
vol_scroll_step: self.scroll_step_spin.get_value(), vol_scroll_step: self.scroll_step_spin.get_value(),
vol_fine_scroll_step: self.fine_scroll_step_spin.get_value(),
middle_click_action: self.middle_click_combo.get_active().into(), middle_click_action: self.middle_click_combo.get_active().into(),
custom_command, custom_command,
}; };
@ -234,6 +239,11 @@ pub fn show_prefs_dialog(appstate: &Rc<AppS>) {
pub fn init_prefs_callback(appstate: Rc<AppS>) { pub fn init_prefs_callback(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| {
/* skip if prefs window is not present */
if apps.gui.prefs_dialog.borrow().is_none() {
return;
}
match (s, u) { match (s, u) {
(AudioSignal::CardInitialized, _) => (), (AudioSignal::CardInitialized, _) => (),
(AudioSignal::CardCleanedUp, _) => { (AudioSignal::CardCleanedUp, _) => {

View File

@ -1,9 +1,11 @@
use app_state::*; use app_state::*;
use audio::*; use audio::*;
use errors::*; use errors::*;
use glib;
use gio;
use gdk; use gdk;
use gdk_pixbuf; use gdk_pixbuf;
use gdk_pixbuf_sys::GDK_COLORSPACE_RGB; use gdk_pixbuf_sys;
use gtk::prelude::*; use gtk::prelude::*;
use gtk; use gtk;
use prefs::{Prefs, MiddleClickAction}; use prefs::{Prefs, MiddleClickAction};
@ -12,6 +14,7 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use support_cmd::*; use support_cmd::*;
use support_ui::*; use support_ui::*;
use ui_prefs_dialog::show_prefs_dialog;
// TODO tooltip // TODO tooltip
@ -78,6 +81,36 @@ impl TrayIcon {
} }
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()
.map(|s| format!("{}", s.round()))
.unwrap_or(String::from("unknown volume"));
let mute_info = {
if !audio.has_mute() {
"\nNo mute switch"
} else if audio.get_mute().unwrap_or(false) {
"\nMuted"
} else {
""
}
};
self.status_icon.set_tooltip_text(format!("{} ({})\nVolume: {}{}",
cardname,
channame,
vol,
mute_info)
.as_str());
}
pub fn update_all(&self, pub fn update_all(&self,
prefs: &Prefs, prefs: &Prefs,
audio: &Audio, audio: &Audio,
@ -104,6 +137,7 @@ impl TrayIcon {
*self.volmeter.borrow_mut() = Some(volmeter); *self.volmeter.borrow_mut() = Some(volmeter);
} }
self.update_tooltip(&audio);
return self.update_pixbuf(audio.vol()?, audio.vol_level()); return self.update_pixbuf(audio.vol()?, audio.vol_level());
} }
} }
@ -142,7 +176,7 @@ impl VolMeter {
pixbuf: &gdk_pixbuf::Pixbuf) pixbuf: &gdk_pixbuf::Pixbuf)
-> Result<gdk_pixbuf::Pixbuf> { -> Result<gdk_pixbuf::Pixbuf> {
ensure!(pixbuf.get_colorspace() == GDK_COLORSPACE_RGB, ensure!(pixbuf.get_colorspace() == gdk_pixbuf_sys::GDK_COLORSPACE_RGB,
"Invalid colorspace in pixbuf"); "Invalid colorspace in pixbuf");
ensure!(pixbuf.get_bits_per_sample() == 8, ensure!(pixbuf.get_bits_per_sample() == 8,
"Invalid bits per sample in pixbuf"); "Invalid bits per sample in pixbuf");
@ -310,6 +344,7 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
let apps = appstate.clone(); let apps = appstate.clone();
appstate.audio.connect_handler(Box::new(move |s, u| match (s, u) { appstate.audio.connect_handler(Box::new(move |s, u| match (s, u) {
(_, _) => { (_, _) => {
apps.gui.tray_icon.update_tooltip(&apps.audio);
try_w!(apps.gui.tray_icon.update_audio(&apps.audio)); try_w!(apps.gui.tray_icon.update_audio(&apps.audio));
} }
})); }));
@ -400,7 +435,7 @@ fn on_tray_icon_scroll_event(appstate: &AppS,
} }
fn on_tray_button_release_event(appstate: &AppS, fn on_tray_button_release_event(appstate: &Rc<AppS>,
event_button: &gdk::EventButton) event_button: &gdk::EventButton)
-> bool { -> bool {
let button = event_button.get_button(); let button = event_button.get_button();
@ -421,7 +456,8 @@ fn on_tray_button_release_event(appstate: &AppS,
try_wr!(audio.toggle_mute(AudioUser::Popup), false); try_wr!(audio.toggle_mute(AudioUser::Popup), false);
} }
} }
&MiddleClickAction::ShowPreferences => (), // TODO
&MiddleClickAction::ShowPreferences => show_prefs_dialog(&appstate),
&MiddleClickAction::VolumeControl => { &MiddleClickAction::VolumeControl => {
try_wr!(execute_vol_control_command(&appstate.prefs.borrow()), try_wr!(execute_vol_control_command(&appstate.prefs.borrow()),
false); false);