Lalala
This commit is contained in:
parent
a6201df328
commit
9de9a6abaa
@ -9,6 +9,7 @@ alsa-sys = "^0.1.1"
|
|||||||
libc = "^0.2.23"
|
libc = "^0.2.23"
|
||||||
gdk-sys = { git = "https://github.com/gtk-rs/sys" }
|
gdk-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||||
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" }
|
||||||
gtk-sys = { git = "https://github.com/gtk-rs/sys" }
|
gtk-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||||
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" }
|
||||||
|
@ -38,10 +38,11 @@ pub struct AlsaCard {
|
|||||||
|
|
||||||
|
|
||||||
impl AlsaCard {
|
impl AlsaCard {
|
||||||
pub fn new(card_name: Option<String>,
|
pub fn new(
|
||||||
elem_name: Option<String>,
|
card_name: Option<String>,
|
||||||
cb: Rc<Fn(AlsaEvent)>)
|
elem_name: Option<String>,
|
||||||
-> Result<Box<AlsaCard>> {
|
cb: Rc<Fn(AlsaEvent)>,
|
||||||
|
) -> Result<Box<AlsaCard>> {
|
||||||
let card = {
|
let card = {
|
||||||
match card_name {
|
match card_name {
|
||||||
Some(name) => get_alsa_card_by_name(name)?,
|
Some(name) => get_alsa_card_by_name(name)?,
|
||||||
@ -52,26 +53,26 @@ impl AlsaCard {
|
|||||||
|
|
||||||
let vec_pollfd = PollDescriptors::get(&mixer)?;
|
let vec_pollfd = PollDescriptors::get(&mixer)?;
|
||||||
|
|
||||||
let selem_id =
|
let selem_id = get_selem_by_name(
|
||||||
get_selem_by_name(&mixer,
|
&mixer,
|
||||||
elem_name.unwrap_or(String::from("Master")))
|
elem_name.unwrap_or(String::from("Master")),
|
||||||
.unwrap()
|
).unwrap()
|
||||||
.get_id();
|
.get_id();
|
||||||
|
|
||||||
let acard = Box::new(AlsaCard {
|
let acard = Box::new(AlsaCard {
|
||||||
_cannot_construct: (),
|
_cannot_construct: (),
|
||||||
card: card,
|
card: card,
|
||||||
mixer: mixer,
|
mixer: mixer,
|
||||||
selem_id: selem_id,
|
selem_id: selem_id,
|
||||||
watch_ids: Cell::new(vec![]),
|
watch_ids: Cell::new(vec![]),
|
||||||
cb: cb,
|
cb: cb,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* TODO: callback is registered here, which must be unregistered
|
/* TODO: callback is registered here, which must be unregistered
|
||||||
* when the card is destroyed!!
|
* when the card is destroyed!!
|
||||||
* poll descriptors must be unwatched too */
|
* poll descriptors must be unwatched too */
|
||||||
let watch_ids = AlsaCard::watch_poll_descriptors(vec_pollfd,
|
let watch_ids =
|
||||||
acard.as_ref());
|
AlsaCard::watch_poll_descriptors(vec_pollfd, acard.as_ref());
|
||||||
acard.watch_ids.set(watch_ids);
|
acard.watch_ids.set(watch_ids);
|
||||||
|
|
||||||
return Ok(acard);
|
return Ok(acard);
|
||||||
@ -84,9 +85,7 @@ impl AlsaCard {
|
|||||||
|
|
||||||
|
|
||||||
pub fn chan_name(&self) -> Result<String> {
|
pub fn chan_name(&self) -> Result<String> {
|
||||||
let n = self.selem_id
|
let n = self.selem_id.get_name().map(|y| String::from(y))?;
|
||||||
.get_name()
|
|
||||||
.map(|y| String::from(y))?;
|
|
||||||
return Ok(n);
|
return Ok(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +114,9 @@ impl AlsaCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let range = selem.get_playback_volume_range();
|
let range = selem.get_playback_volume_range();
|
||||||
selem.set_playback_volume_all(percent_to_vol(new_vol, range))?;
|
selem.set_playback_volume_all(
|
||||||
|
percent_to_vol(new_vol, range),
|
||||||
|
)?;
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -142,9 +143,10 @@ impl AlsaCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn watch_poll_descriptors(polls: Vec<pollfd>,
|
fn watch_poll_descriptors(
|
||||||
acard: &AlsaCard)
|
polls: Vec<pollfd>,
|
||||||
-> Vec<c_uint> {
|
acard: &AlsaCard,
|
||||||
|
) -> Vec<c_uint> {
|
||||||
let mut watch_ids: Vec<c_uint> = vec![];
|
let mut watch_ids: Vec<c_uint> = vec![];
|
||||||
let acard_ptr =
|
let acard_ptr =
|
||||||
unsafe { mem::transmute::<&AlsaCard, glib_sys::gpointer>(acard) };
|
unsafe { mem::transmute::<&AlsaCard, glib_sys::gpointer>(acard) };
|
||||||
@ -201,10 +203,11 @@ impl Drop for AlsaCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
|
extern "C" fn watch_cb(
|
||||||
cond: glib_sys::GIOCondition,
|
chan: *mut glib_sys::GIOChannel,
|
||||||
data: glib_sys::gpointer)
|
cond: glib_sys::GIOCondition,
|
||||||
-> glib_sys::gboolean {
|
data: glib_sys::gpointer,
|
||||||
|
) -> glib_sys::gboolean {
|
||||||
|
|
||||||
let acard =
|
let acard =
|
||||||
unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) };
|
unsafe { mem::transmute::<glib_sys::gpointer, &AlsaCard>(data) };
|
||||||
@ -223,14 +226,15 @@ extern "C" fn watch_cb(chan: *mut glib_sys::GIOChannel,
|
|||||||
let mut buf: Vec<u8> = vec![0; 256];
|
let mut buf: Vec<u8> = vec![0; 256];
|
||||||
|
|
||||||
while sread > 0 {
|
while sread > 0 {
|
||||||
let stat: glib_sys::GIOStatus =
|
let stat: glib_sys::GIOStatus = unsafe {
|
||||||
unsafe {
|
glib_sys::g_io_channel_read_chars(
|
||||||
glib_sys::g_io_channel_read_chars(chan,
|
chan,
|
||||||
buf.as_mut_ptr() as *mut u8,
|
buf.as_mut_ptr() as *mut u8,
|
||||||
256,
|
256,
|
||||||
&mut sread as *mut size_t,
|
&mut sread as *mut size_t,
|
||||||
ptr::null_mut())
|
ptr::null_mut(),
|
||||||
};
|
)
|
||||||
|
};
|
||||||
|
|
||||||
match stat {
|
match stat {
|
||||||
glib_sys::G_IO_STATUS_AGAIN => {
|
glib_sys::G_IO_STATUS_AGAIN => {
|
||||||
@ -324,9 +328,7 @@ pub fn get_selem_names(mixer: &Mixer) -> Vec<String> {
|
|||||||
|
|
||||||
pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
|
pub fn get_selem_by_name(mixer: &Mixer, name: String) -> Result<Selem> {
|
||||||
for selem in get_selems(mixer) {
|
for selem in get_selems(mixer) {
|
||||||
let n = selem.get_id()
|
let n = selem.get_id().get_name().map(|y| String::from(y))?;
|
||||||
.get_name()
|
|
||||||
.map(|y| String::from(y))?;
|
|
||||||
|
|
||||||
if n == name {
|
if n == name {
|
||||||
return Ok(selem);
|
return Ok(selem);
|
||||||
|
@ -14,15 +14,17 @@ pub struct AppS {
|
|||||||
|
|
||||||
impl AppS {
|
impl AppS {
|
||||||
pub fn new() -> AppS {
|
pub fn new() -> AppS {
|
||||||
let builder_popup_window =
|
let builder_popup_window = gtk::Builder::new_from_string(include_str!(
|
||||||
gtk::Builder::new_from_string(include_str!("../data/ui/popup-window.glade"));
|
"../data/ui/popup-window.glade"
|
||||||
let builder_popup_menu = gtk::Builder::new_from_string(include_str!("../data/ui/popup-menu.glade"));
|
));
|
||||||
|
let builder_popup_menu = gtk::Builder::new_from_string(
|
||||||
|
include_str!("../data/ui/popup-menu.glade"),
|
||||||
|
);
|
||||||
|
|
||||||
return AppS {
|
return AppS {
|
||||||
gui: Gui::new(builder_popup_window, builder_popup_menu),
|
gui: Gui::new(builder_popup_window, builder_popup_menu),
|
||||||
audio: Audio::new(None, Some(String::from("Master")))
|
audio: Audio::new(None, Some(String::from("Master"))).unwrap(),
|
||||||
.unwrap(),
|
};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,28 +38,33 @@ pub struct Gui {
|
|||||||
|
|
||||||
|
|
||||||
impl Gui {
|
impl Gui {
|
||||||
pub fn new(builder_popup_window: gtk::Builder,
|
pub fn new(
|
||||||
builder_popup_menu: gtk::Builder)
|
builder_popup_window: gtk::Builder,
|
||||||
-> Gui {
|
builder_popup_menu: gtk::Builder,
|
||||||
|
) -> Gui {
|
||||||
return Gui {
|
return Gui {
|
||||||
status_icon: gtk::StatusIcon::new(),
|
status_icon: gtk::StatusIcon::new(),
|
||||||
popup_window: PopupWindow::new(builder_popup_window),
|
popup_window: PopupWindow::new(builder_popup_window),
|
||||||
popup_menu: PopupMenu::new(builder_popup_menu),
|
popup_menu: PopupMenu::new(builder_popup_menu),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
create_builder_item!(PopupWindow,
|
create_builder_item!(
|
||||||
popup_window: gtk::Window,
|
PopupWindow,
|
||||||
vol_scale_adj: gtk::Adjustment,
|
popup_window: gtk::Window,
|
||||||
vol_scale: gtk::Scale,
|
vol_scale_adj: gtk::Adjustment,
|
||||||
mute_check: gtk::CheckButton);
|
vol_scale: gtk::Scale,
|
||||||
|
mute_check: gtk::CheckButton
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
create_builder_item!(PopupMenu,
|
create_builder_item!(
|
||||||
menu_window: gtk::Window,
|
PopupMenu,
|
||||||
menubar: gtk::MenuBar,
|
menu_window: gtk::Window,
|
||||||
menu: gtk::Menu,
|
menubar: gtk::MenuBar,
|
||||||
about_item: gtk::MenuItem,
|
menu: gtk::Menu,
|
||||||
prefs_item: gtk::MenuItem);
|
about_item: gtk::MenuItem,
|
||||||
|
prefs_item: gtk::MenuItem
|
||||||
|
);
|
||||||
|
207
src/audio.rs
207
src/audio.rs
@ -72,9 +72,10 @@ pub struct Audio {
|
|||||||
|
|
||||||
|
|
||||||
impl Audio {
|
impl Audio {
|
||||||
pub fn new(card_name: Option<String>,
|
pub fn new(
|
||||||
elem_name: Option<String>)
|
card_name: Option<String>,
|
||||||
-> Result<Audio> {
|
elem_name: Option<String>,
|
||||||
|
) -> Result<Audio> {
|
||||||
|
|
||||||
let handlers = Handlers::new();
|
let handlers = Handlers::new();
|
||||||
let last_action_timestamp = Rc::new(RefCell::new(0));
|
let last_action_timestamp = Rc::new(RefCell::new(0));
|
||||||
@ -83,10 +84,8 @@ impl Audio {
|
|||||||
let myhandler = handlers.clone();
|
let myhandler = handlers.clone();
|
||||||
let ts = last_action_timestamp.clone();
|
let ts = last_action_timestamp.clone();
|
||||||
Rc::new(move |event| {
|
Rc::new(move |event| {
|
||||||
on_alsa_event(&mut *ts.borrow_mut(),
|
on_alsa_event(&mut *ts.borrow_mut(), &myhandler.borrow(), event)
|
||||||
&myhandler.borrow(),
|
})
|
||||||
event)
|
|
||||||
})
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let audio = Audio {
|
let audio = Audio {
|
||||||
@ -101,39 +100,33 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn switch_acard(&self,
|
pub fn switch_acard(
|
||||||
card_name: Option<String>,
|
&self,
|
||||||
elem_name: Option<String>)
|
card_name: Option<String>,
|
||||||
-> Result<()> {
|
elem_name: Option<String>,
|
||||||
|
) -> Result<()> {
|
||||||
debug!("Switching cards");
|
debug!("Switching cards");
|
||||||
debug!("Old card name: {}",
|
debug!(
|
||||||
self.acard
|
"Old card name: {}",
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap()
|
||||||
.card_name()
|
);
|
||||||
.unwrap());
|
debug!(
|
||||||
debug!("Old chan name: {}",
|
"Old chan name: {}",
|
||||||
self.acard
|
self.acard.borrow().chan_name().unwrap()
|
||||||
.borrow()
|
);
|
||||||
.chan_name()
|
let cb = self.acard.borrow().cb.clone();
|
||||||
.unwrap());
|
|
||||||
let cb = self.acard
|
|
||||||
.borrow()
|
|
||||||
.cb
|
|
||||||
.clone();
|
|
||||||
{
|
{
|
||||||
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: {}",
|
debug!(
|
||||||
self.acard
|
"Old card name: {}",
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap()
|
||||||
.card_name()
|
);
|
||||||
.unwrap());
|
debug!(
|
||||||
debug!("Old chan name: {}",
|
"Old chan name: {}",
|
||||||
self.acard
|
self.acard.borrow().chan_name().unwrap()
|
||||||
.borrow()
|
);
|
||||||
.chan_name()
|
|
||||||
.unwrap());
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
// TODO: invoke handler
|
// TODO: invoke handler
|
||||||
}
|
}
|
||||||
@ -166,24 +159,20 @@ impl Audio {
|
|||||||
*rc = glib::get_monotonic_time();
|
*rc = glib::get_monotonic_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
|
debug!(
|
||||||
self.acard
|
"Setting vol on card {:?} and chan {:?} to {:?} by user {:?}",
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap(),
|
||||||
.card_name()
|
self.acard.borrow().chan_name().unwrap(),
|
||||||
.unwrap(),
|
new_vol,
|
||||||
self.acard
|
user
|
||||||
.borrow()
|
);
|
||||||
.chan_name()
|
self.acard.borrow().set_vol(new_vol)?;
|
||||||
.unwrap(),
|
|
||||||
new_vol,
|
|
||||||
user);
|
|
||||||
self.acard
|
|
||||||
.borrow()
|
|
||||||
.set_vol(new_vol)?;
|
|
||||||
|
|
||||||
invoke_handlers(&self.handlers.borrow(),
|
invoke_handlers(
|
||||||
AudioSignal::ValuesChanged,
|
&self.handlers.borrow(),
|
||||||
user);
|
AudioSignal::ValuesChanged,
|
||||||
|
user,
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,23 +185,21 @@ impl Audio {
|
|||||||
let old_vol = self.vol()?;
|
let old_vol = self.vol()?;
|
||||||
let new_vol = f64::ceil(old_vol + (self.scroll_step.get() as f64));
|
let new_vol = f64::ceil(old_vol + (self.scroll_step.get() as f64));
|
||||||
|
|
||||||
debug!("Increase vol on card {:?} and chan {:?} by {:?} to {:?}",
|
debug!(
|
||||||
self.acard
|
"Increase vol on card {:?} and chan {:?} by {:?} to {:?}",
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap(),
|
||||||
.card_name()
|
self.acard.borrow().chan_name().unwrap(),
|
||||||
.unwrap(),
|
(new_vol - old_vol),
|
||||||
self.acard
|
new_vol
|
||||||
.borrow()
|
);
|
||||||
.chan_name()
|
|
||||||
.unwrap(),
|
|
||||||
(new_vol - old_vol),
|
|
||||||
new_vol);
|
|
||||||
|
|
||||||
self.set_vol(new_vol, user)?;
|
self.set_vol(new_vol, user)?;
|
||||||
|
|
||||||
invoke_handlers(&self.handlers.borrow(),
|
invoke_handlers(
|
||||||
AudioSignal::ValuesChanged,
|
&self.handlers.borrow(),
|
||||||
user);
|
AudioSignal::ValuesChanged,
|
||||||
|
user,
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,23 +212,21 @@ impl Audio {
|
|||||||
let old_vol = self.vol()?;
|
let old_vol = self.vol()?;
|
||||||
let new_vol = old_vol - (self.scroll_step.get() as f64);
|
let new_vol = old_vol - (self.scroll_step.get() as f64);
|
||||||
|
|
||||||
debug!("Decrease vol on card {:?} and chan {:?} by {:?} to {:?}",
|
debug!(
|
||||||
self.acard
|
"Decrease vol on card {:?} and chan {:?} by {:?} to {:?}",
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap(),
|
||||||
.card_name()
|
self.acard.borrow().chan_name().unwrap(),
|
||||||
.unwrap(),
|
(new_vol - old_vol),
|
||||||
self.acard
|
new_vol
|
||||||
.borrow()
|
);
|
||||||
.chan_name()
|
|
||||||
.unwrap(),
|
|
||||||
(new_vol - old_vol),
|
|
||||||
new_vol);
|
|
||||||
|
|
||||||
self.set_vol(new_vol, user)?;
|
self.set_vol(new_vol, user)?;
|
||||||
|
|
||||||
invoke_handlers(&self.handlers.borrow(),
|
invoke_handlers(
|
||||||
AudioSignal::ValuesChanged,
|
&self.handlers.borrow(),
|
||||||
user);
|
AudioSignal::ValuesChanged,
|
||||||
|
user,
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,25 +245,21 @@ impl Audio {
|
|||||||
let mut rc = self.last_action_timestamp.borrow_mut();
|
let mut rc = self.last_action_timestamp.borrow_mut();
|
||||||
*rc = glib::get_monotonic_time();
|
*rc = glib::get_monotonic_time();
|
||||||
|
|
||||||
debug!("Setting mute to {} on card {:?} and chan {:?} by user {:?}",
|
debug!(
|
||||||
mute,
|
"Setting mute to {} on card {:?} and chan {:?} by user {:?}",
|
||||||
self.acard
|
mute,
|
||||||
.borrow()
|
self.acard.borrow().card_name().unwrap(),
|
||||||
.card_name()
|
self.acard.borrow().chan_name().unwrap(),
|
||||||
.unwrap(),
|
user
|
||||||
self.acard
|
);
|
||||||
.borrow()
|
|
||||||
.chan_name()
|
|
||||||
.unwrap(),
|
|
||||||
user);
|
|
||||||
|
|
||||||
self.acard
|
self.acard.borrow().set_mute(mute)?;
|
||||||
.borrow()
|
|
||||||
.set_mute(mute)?;
|
|
||||||
|
|
||||||
invoke_handlers(&self.handlers.borrow(),
|
invoke_handlers(
|
||||||
AudioSignal::ValuesChanged,
|
&self.handlers.borrow(),
|
||||||
user);
|
AudioSignal::ValuesChanged,
|
||||||
|
user,
|
||||||
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,12 +276,16 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
fn invoke_handlers(
|
||||||
signal: AudioSignal,
|
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
||||||
user: AudioUser) {
|
signal: AudioSignal,
|
||||||
debug!("Invoking handlers for signal {:?} by user {:?}",
|
user: AudioUser,
|
||||||
signal,
|
) {
|
||||||
user);
|
debug!(
|
||||||
|
"Invoking handlers for signal {:?} by user {:?}",
|
||||||
|
signal,
|
||||||
|
user
|
||||||
|
);
|
||||||
for handler in handlers {
|
for handler in handlers {
|
||||||
let unboxed = handler.as_ref();
|
let unboxed = handler.as_ref();
|
||||||
unboxed(signal, user);
|
unboxed(signal, user);
|
||||||
@ -308,9 +293,11 @@ fn invoke_handlers(handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_alsa_event(last_action_timestamp: &mut i64,
|
fn on_alsa_event(
|
||||||
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
last_action_timestamp: &mut i64,
|
||||||
alsa_event: AlsaEvent) {
|
handlers: &Vec<Box<Fn(AudioSignal, AudioUser)>>,
|
||||||
|
alsa_event: AlsaEvent,
|
||||||
|
) {
|
||||||
let last: i64 = *last_action_timestamp;
|
let last: i64 = *last_action_timestamp;
|
||||||
|
|
||||||
if last != 0 {
|
if last != 0 {
|
||||||
@ -330,9 +317,11 @@ fn on_alsa_event(last_action_timestamp: &mut i64,
|
|||||||
AlsaEvent::AlsaCardDiconnected => debug!("AlsaCardDiconnected"),
|
AlsaEvent::AlsaCardDiconnected => debug!("AlsaCardDiconnected"),
|
||||||
AlsaEvent::AlsaCardValuesChanged => {
|
AlsaEvent::AlsaCardValuesChanged => {
|
||||||
debug!("AlsaCardValuesChanged");
|
debug!("AlsaCardValuesChanged");
|
||||||
invoke_handlers(handlers,
|
invoke_handlers(
|
||||||
self::AudioSignal::ValuesChanged,
|
handlers,
|
||||||
self::AudioUser::Unknown);
|
self::AudioSignal::ValuesChanged,
|
||||||
|
self::AudioUser::Unknown,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
e => warn!("Unhandled alsa event: {:?}", e),
|
e => warn!("Unhandled alsa event: {:?}", e),
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ extern crate alsa_sys;
|
|||||||
extern crate ffi;
|
extern crate ffi;
|
||||||
extern crate gdk;
|
extern crate gdk;
|
||||||
extern crate gdk_pixbuf;
|
extern crate gdk_pixbuf;
|
||||||
|
extern crate gdk_pixbuf_sys;
|
||||||
extern crate gdk_sys;
|
extern crate gdk_sys;
|
||||||
extern crate glib;
|
extern crate glib;
|
||||||
extern crate glib_sys;
|
extern crate glib_sys;
|
||||||
@ -52,7 +53,6 @@ fn main() {
|
|||||||
.unwrap_or_else(|e| panic!("Logger initialization failed with {}", e));
|
.unwrap_or_else(|e| panic!("Logger initialization failed with {}", e));
|
||||||
|
|
||||||
let apps = Rc::new(AppS::new());
|
let apps = Rc::new(AppS::new());
|
||||||
// let _ = apps.audio.switch_acard(None, None).unwrap();
|
|
||||||
|
|
||||||
ui_entry::init(apps);
|
ui_entry::init(apps);
|
||||||
|
|
||||||
|
@ -11,14 +11,15 @@ use ui_prefs_dialog::*;
|
|||||||
pub fn init(appstate: Rc<AppS>) {
|
pub fn init(appstate: Rc<AppS>) {
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| match (s, u) {
|
appstate.audio.connect_handler(
|
||||||
(AudioSignal::ValuesChanged,
|
Box::new(move |s, u| match (s, u) {
|
||||||
AudioUser::Unknown) => {
|
(AudioSignal::ValuesChanged, AudioUser::Unknown) => {
|
||||||
debug!("External volume change!");
|
debug!("External volume change!");
|
||||||
|
|
||||||
}
|
}
|
||||||
_ => debug!("Nix"),
|
_ => debug!("Nix"),
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_tray_icon(appstate.clone());
|
init_tray_icon(appstate.clone());
|
||||||
|
@ -14,24 +14,18 @@ pub fn init_popup_menu(appstate: Rc<AppS>) {
|
|||||||
/* about_item.connect_activate_link */
|
/* about_item.connect_activate_link */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let about_item = &appstate.clone()
|
let about_item = &appstate.clone().gui.popup_menu.about_item;
|
||||||
.gui
|
about_item.connect_activate(
|
||||||
.popup_menu
|
move |_| { on_about_item_activate(&apps); },
|
||||||
.about_item;
|
);
|
||||||
about_item.connect_activate(move |_| {
|
|
||||||
on_about_item_activate(&apps);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* about_item.connect_activate_link */
|
/* about_item.connect_activate_link */
|
||||||
{
|
{
|
||||||
let prefs_item = &appstate.clone()
|
let prefs_item = &appstate.clone().gui.popup_menu.prefs_item;
|
||||||
.gui
|
prefs_item.connect_activate(
|
||||||
.popup_menu
|
move |_| { on_prefs_item_activate(appstate.clone()); },
|
||||||
.prefs_item;
|
);
|
||||||
prefs_item.connect_activate(move |_| {
|
|
||||||
on_prefs_item_activate(appstate.clone());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +43,8 @@ fn on_about_item_activate(appstate: &AppS) {
|
|||||||
fn create_about_dialog() -> gtk::AboutDialog {
|
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("PNMixer is free software; you can redistribute it and/or modify it
|
about_dialog.set_license(Some(
|
||||||
|
"PNMixer 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.
|
||||||
|
|
||||||
@ -60,7 +55,8 @@ See the GNU General Public License for more details.
|
|||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with PNMixer; if not, write to the Free Software Foundation,
|
along with PNMixer; if not, write to the Free Software Foundation,
|
||||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA."));
|
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"]);
|
||||||
|
@ -18,37 +18,25 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
|
|||||||
/* mute_check.connect_toggled */
|
/* mute_check.connect_toggled */
|
||||||
{
|
{
|
||||||
let _appstate = appstate.clone();
|
let _appstate = appstate.clone();
|
||||||
let mute_check = &appstate.clone()
|
let mute_check = &appstate.clone().gui.popup_window.mute_check;
|
||||||
.gui
|
toggle_signal = mute_check.connect_toggled(
|
||||||
.popup_window
|
move |_| on_mute_check_toggled(&_appstate),
|
||||||
.mute_check;
|
);
|
||||||
toggle_signal =
|
|
||||||
mute_check.connect_toggled(move |_| {
|
|
||||||
on_mute_check_toggled(&_appstate)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* popup_window.connect_show */
|
/* popup_window.connect_show */
|
||||||
{
|
{
|
||||||
let _appstate = appstate.clone();
|
let _appstate = appstate.clone();
|
||||||
let popup_window = &appstate.clone()
|
let popup_window = &appstate.clone().gui.popup_window.popup_window;
|
||||||
.gui
|
popup_window.connect_show(
|
||||||
.popup_window
|
move |w| on_popup_window_show(w, &_appstate, toggle_signal),
|
||||||
.popup_window;
|
);
|
||||||
popup_window.connect_show(move |w| {
|
|
||||||
on_popup_window_show(w,
|
|
||||||
&_appstate,
|
|
||||||
toggle_signal)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vol_scale_adj.connect_value_changed */
|
/* vol_scale_adj.connect_value_changed */
|
||||||
{
|
{
|
||||||
let _appstate = appstate.clone();
|
let _appstate = appstate.clone();
|
||||||
let vol_scale_adj = &appstate.clone()
|
let vol_scale_adj = &appstate.clone().gui.popup_window.vol_scale_adj;
|
||||||
.gui
|
|
||||||
.popup_window
|
|
||||||
.vol_scale_adj;
|
|
||||||
vol_scale_adj.connect_value_changed(
|
vol_scale_adj.connect_value_changed(
|
||||||
move |_| on_vol_scale_value_changed(&_appstate),
|
move |_| on_vol_scale_value_changed(&_appstate),
|
||||||
);
|
);
|
||||||
@ -57,20 +45,19 @@ pub fn init_popup_window(appstate: Rc<AppS>) {
|
|||||||
/* popup_window.connect_event */
|
/* popup_window.connect_event */
|
||||||
{
|
{
|
||||||
let _appstate = appstate.clone();
|
let _appstate = appstate.clone();
|
||||||
let popup_window = &appstate.clone()
|
let popup_window = &appstate.clone().gui.popup_window.popup_window;
|
||||||
.gui
|
|
||||||
.popup_window
|
|
||||||
.popup_window;
|
|
||||||
popup_window.connect_event(move |w, e| {
|
popup_window.connect_event(move |w, e| {
|
||||||
on_popup_window_event(w, e, &_appstate)
|
on_popup_window_event(w, e, &_appstate)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_popup_window_show(window: >k::Window,
|
fn on_popup_window_show(
|
||||||
appstate: &AppS,
|
window: >k::Window,
|
||||||
toggle_signal: u64) {
|
appstate: &AppS,
|
||||||
|
toggle_signal: u64,
|
||||||
|
) {
|
||||||
let audio = &appstate.audio;
|
let audio = &appstate.audio;
|
||||||
let popup_window = &appstate.gui.popup_window;
|
let popup_window = &appstate.gui.popup_window;
|
||||||
|
|
||||||
@ -85,10 +72,11 @@ fn on_popup_window_show(window: >k::Window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_popup_window_event(w: >k::Window,
|
fn on_popup_window_event(
|
||||||
e: &gdk::Event,
|
w: >k::Window,
|
||||||
appstate: &AppS)
|
e: &gdk::Event,
|
||||||
-> gtk::Inhibit {
|
appstate: &AppS,
|
||||||
|
) -> gtk::Inhibit {
|
||||||
match gdk::Event::get_event_type(e) {
|
match gdk::Event::get_event_type(e) {
|
||||||
gdk::EventType::GrabBroken => w.hide(),
|
gdk::EventType::GrabBroken => w.hide(),
|
||||||
gdk::EventType::KeyPress => {
|
gdk::EventType::KeyPress => {
|
||||||
@ -120,10 +108,7 @@ fn on_popup_window_event(w: >k::Window,
|
|||||||
fn on_vol_scale_value_changed(appstate: &AppS) {
|
fn on_vol_scale_value_changed(appstate: &AppS) {
|
||||||
let audio = &appstate.audio;
|
let audio = &appstate.audio;
|
||||||
|
|
||||||
let val = appstate.gui
|
let val = appstate.gui.popup_window.vol_scale.get_value();
|
||||||
.popup_window
|
|
||||||
.vol_scale
|
|
||||||
.get_value();
|
|
||||||
|
|
||||||
try_w!(audio.set_vol(val, AudioUser::Popup));
|
try_w!(audio.set_vol(val, AudioUser::Popup));
|
||||||
}
|
}
|
||||||
@ -135,9 +120,11 @@ fn on_mute_check_toggled(appstate: &AppS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn update_mute_check(appstate: &AppS,
|
pub fn update_mute_check(
|
||||||
toggle_signal: u64,
|
appstate: &AppS,
|
||||||
muted: Result<bool>) {
|
toggle_signal: u64,
|
||||||
|
muted: Result<bool>,
|
||||||
|
) {
|
||||||
let check_button = &appstate.gui.popup_window.mute_check;
|
let check_button = &appstate.gui.popup_window.mute_check;
|
||||||
|
|
||||||
glib::signal_handler_block(check_button, toggle_signal);
|
glib::signal_handler_block(check_button, toggle_signal);
|
||||||
@ -170,32 +157,40 @@ fn grab_devices(window: >k::Window) -> Result<()> {
|
|||||||
let gdk_window = window.get_window().ok_or("No window?!")?;
|
let gdk_window = window.get_window().ok_or("No window?!")?;
|
||||||
|
|
||||||
/* Grab the mouse */
|
/* Grab the mouse */
|
||||||
let m_grab_status =
|
let m_grab_status = device.grab(
|
||||||
device.grab(&gdk_window,
|
&gdk_window,
|
||||||
GrabOwnership::None,
|
GrabOwnership::None,
|
||||||
true,
|
true,
|
||||||
BUTTON_PRESS_MASK,
|
BUTTON_PRESS_MASK,
|
||||||
None,
|
None,
|
||||||
GDK_CURRENT_TIME as u32);
|
GDK_CURRENT_TIME as u32,
|
||||||
|
);
|
||||||
|
|
||||||
if m_grab_status != GrabStatus::Success {
|
if m_grab_status != GrabStatus::Success {
|
||||||
warn!("Could not grab {}",
|
warn!(
|
||||||
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
|
"Could not grab {}",
|
||||||
|
device.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grab the keyboard */
|
/* Grab the keyboard */
|
||||||
let k_dev = device.get_associated_device()
|
let k_dev = device.get_associated_device().ok_or(
|
||||||
.ok_or("Couldn't get associated device")?;
|
"Couldn't get associated device",
|
||||||
|
)?;
|
||||||
|
|
||||||
let k_grab_status = k_dev.grab(&gdk_window,
|
let k_grab_status = k_dev.grab(
|
||||||
GrabOwnership::None,
|
&gdk_window,
|
||||||
true,
|
GrabOwnership::None,
|
||||||
KEY_PRESS_MASK,
|
true,
|
||||||
None,
|
KEY_PRESS_MASK,
|
||||||
GDK_CURRENT_TIME as u32);
|
None,
|
||||||
|
GDK_CURRENT_TIME as u32,
|
||||||
|
);
|
||||||
if k_grab_status != GrabStatus::Success {
|
if k_grab_status != GrabStatus::Success {
|
||||||
warn!("Could not grab {}",
|
warn!(
|
||||||
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE")));
|
"Could not grab {}",
|
||||||
|
k_dev.get_name().unwrap_or(String::from("UNKNOWN DEVICE"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -5,16 +5,20 @@ use gtk;
|
|||||||
use alsa_pn;
|
use alsa_pn;
|
||||||
|
|
||||||
|
|
||||||
create_builder_item!(PrefsDialog,
|
create_builder_item!(
|
||||||
prefs_dialog: gtk::Dialog,
|
PrefsDialog,
|
||||||
card_combo: gtk::ComboBoxText,
|
prefs_dialog: gtk::Dialog,
|
||||||
chan_combo: gtk::ComboBoxText);
|
card_combo: gtk::ComboBoxText,
|
||||||
|
chan_combo: gtk::ComboBoxText
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn show_prefs_dialog(appstate: Rc<AppS>) {
|
pub fn show_prefs_dialog(appstate: Rc<AppS>) {
|
||||||
let builder_prefs_dialog = gtk::Builder::new_from_string(include_str!("../data/ui/prefs-dialog.glade"));
|
let builder_prefs_dialog = gtk::Builder::new_from_string(
|
||||||
|
include_str!("../data/ui/prefs-dialog.glade"),
|
||||||
|
);
|
||||||
let prefs_dialog = Rc::new(PrefsDialog::new(builder_prefs_dialog));
|
let prefs_dialog = Rc::new(PrefsDialog::new(builder_prefs_dialog));
|
||||||
init_prefs_dialog(&appstate, &prefs_dialog);
|
init_prefs_dialog(&appstate, &prefs_dialog);
|
||||||
{
|
{
|
||||||
@ -33,7 +37,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>, prefs_dialog: &Rc<PrefsDialog>) {
|
|||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let pd = prefs_dialog.clone();
|
let pd = prefs_dialog.clone();
|
||||||
prefs_dialog.clone().prefs_dialog.connect_show(move |_| { on_prefs_dialog_show(&apps, &pd); });
|
prefs_dialog.clone().prefs_dialog.connect_show(move |_| {
|
||||||
|
on_prefs_dialog_show(&apps, &pd);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* DEVICE TAB */
|
/* DEVICE TAB */
|
||||||
@ -44,9 +50,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>, prefs_dialog: &Rc<PrefsDialog>) {
|
|||||||
let card_combo = &prefs_dialog.card_combo;
|
let card_combo = &prefs_dialog.card_combo;
|
||||||
let pd = prefs_dialog.clone();
|
let pd = prefs_dialog.clone();
|
||||||
|
|
||||||
card_combo.connect_changed(move |_| {
|
card_combo.connect_changed(
|
||||||
on_card_combo_changed(&apps, &pd);
|
move |_| { on_card_combo_changed(&apps, &pd); },
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
/* card_combo.connect_changed */
|
/* card_combo.connect_changed */
|
||||||
{
|
{
|
||||||
@ -54,9 +60,9 @@ pub fn init_prefs_dialog(appstate: &Rc<AppS>, prefs_dialog: &Rc<PrefsDialog>) {
|
|||||||
let chan_combo = &prefs_dialog.chan_combo;
|
let chan_combo = &prefs_dialog.chan_combo;
|
||||||
let pd = prefs_dialog.clone();
|
let pd = prefs_dialog.clone();
|
||||||
|
|
||||||
chan_combo.connect_changed(move |_| {
|
chan_combo.connect_changed(
|
||||||
on_chan_combo_changed(&apps, &pd);
|
move |_| { on_chan_combo_changed(&apps, &pd); },
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +74,8 @@ fn on_prefs_dialog_show(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
|||||||
|
|
||||||
|
|
||||||
/* set card combo */
|
/* set card combo */
|
||||||
let cur_card_name = try_w!(acard.card_name(),
|
let cur_card_name =
|
||||||
"Can't get current card name!");
|
try_w!(acard.card_name(), "Can't get current card name!");
|
||||||
let available_card_names = alsa_pn::get_alsa_card_names();
|
let available_card_names = alsa_pn::get_alsa_card_names();
|
||||||
|
|
||||||
/* set_active_id doesn't work, so save the index */
|
/* set_active_id doesn't work, so save the index */
|
||||||
@ -110,8 +116,9 @@ fn on_prefs_dialog_show(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
|||||||
fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
||||||
let card_combo = &prefs_dialog.card_combo;
|
let card_combo = &prefs_dialog.card_combo;
|
||||||
let chan_combo = &prefs_dialog.chan_combo;
|
let chan_combo = &prefs_dialog.chan_combo;
|
||||||
let active_card_item =
|
let active_card_item = try_w!(card_combo.get_active_text().ok_or(
|
||||||
try_w!(card_combo.get_active_text().ok_or("No active Card item found"));
|
"No active Card item found",
|
||||||
|
));
|
||||||
let active_chan_item = chan_combo.get_active_id();
|
let active_chan_item = chan_combo.get_active_id();
|
||||||
let cur_card_name = {
|
let cur_card_name = {
|
||||||
let acard = appstate.audio.acard.borrow();
|
let acard = appstate.audio.acard.borrow();
|
||||||
@ -119,7 +126,10 @@ fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if active_card_item != cur_card_name {
|
if active_card_item != cur_card_name {
|
||||||
appstate.audio.switch_acard(Some(cur_card_name), active_chan_item);
|
appstate.audio.switch_acard(
|
||||||
|
Some(cur_card_name),
|
||||||
|
active_chan_item,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +137,9 @@ fn on_card_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
|||||||
fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
||||||
let card_combo = &prefs_dialog.card_combo;
|
let card_combo = &prefs_dialog.card_combo;
|
||||||
let chan_combo = &prefs_dialog.chan_combo;
|
let chan_combo = &prefs_dialog.chan_combo;
|
||||||
let active_chan_item =
|
let active_chan_item = try_w!(chan_combo.get_active_text().ok_or(
|
||||||
try_w!(chan_combo.get_active_text().ok_or("No active Chan item found"));
|
"No active Chan item found",
|
||||||
|
));
|
||||||
let cur_card_name = {
|
let cur_card_name = {
|
||||||
let acard = appstate.audio.acard.borrow();
|
let acard = appstate.audio.acard.borrow();
|
||||||
acard.card_name().ok()
|
acard.card_name().ok()
|
||||||
@ -139,6 +150,9 @@ fn on_chan_combo_changed(appstate: &AppS, prefs_dialog: &PrefsDialog) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if active_chan_item != cur_chan_name {
|
if active_chan_item != cur_chan_name {
|
||||||
appstate.audio.switch_acard(cur_card_name, Some(active_chan_item));
|
appstate.audio.switch_acard(
|
||||||
|
cur_card_name,
|
||||||
|
Some(active_chan_item),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use app_state::*;
|
use app_state::*;
|
||||||
use gdk;
|
use gdk;
|
||||||
use gdk_pixbuf;
|
use gdk_pixbuf;
|
||||||
|
use gdk_pixbuf_sys;
|
||||||
|
use gdk_pixbuf_sys::GDK_COLORSPACE_RGB;
|
||||||
use gdk_sys;
|
use gdk_sys;
|
||||||
use glib;
|
use glib;
|
||||||
use glib_sys;
|
use glib_sys;
|
||||||
@ -8,45 +10,160 @@ use gtk;
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::cell::Cell;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use libc;
|
use libc;
|
||||||
use audio::*;
|
use audio::*;
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use std::path::*;
|
use std::path::*;
|
||||||
|
use glib::translate::ToGlibPtr;
|
||||||
|
use glib::translate::FromGlibPtrFull;
|
||||||
|
use glib::translate::FromGlibPtrNone;
|
||||||
|
|
||||||
|
use libc::memcpy;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ICON_MIN_SIZE: i32 = 16;
|
const ICON_MIN_SIZE: i32 = 16;
|
||||||
|
|
||||||
|
|
||||||
// struct VolMeter {
|
fn copy_pixbuf(pixbuf: &gdk_pixbuf::Pixbuf) -> gdk_pixbuf::Pixbuf {
|
||||||
// pub red: u8,
|
|
||||||
// pub green: u8,
|
let new_pixbuf = unsafe {
|
||||||
// pub blue: u8,
|
let gdk_pixbuf = pixbuf.to_glib_full();
|
||||||
// pub x_offset_pct: i64,
|
let copy = gdk_pixbuf_sys::gdk_pixbuf_copy(gdk_pixbuf);
|
||||||
// pub y_offset_pct: i64,
|
FromGlibPtrFull::from_glib_full(copy)
|
||||||
// /* dynamic */
|
};
|
||||||
// pub pixbuf: gdk_pixbuf::Pixbuf,
|
|
||||||
// pub width: i64,
|
return new_pixbuf;
|
||||||
// pub row: u8,
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// impl VolMeter {
|
struct VolMeter {
|
||||||
// pub fn new() -> VolMeter {
|
pub red: u8,
|
||||||
// let pixbux = Pixbuf::new();
|
pub green: u8,
|
||||||
// return VolMeter {
|
pub blue: u8,
|
||||||
// red: 255,
|
pub x_offset_pct: i64,
|
||||||
// green: 255,
|
pub y_offset_pct: i64,
|
||||||
// blue: 255,
|
/* dynamic */
|
||||||
// x_offset_pct: 0,
|
pub width: Cell<i64>,
|
||||||
// y_offset_pct: 0,
|
pub row: RefCell<Vec<u8>>,
|
||||||
// pixbuf: ,
|
}
|
||||||
// width: ,
|
|
||||||
// row: ,
|
|
||||||
// }
|
impl VolMeter {
|
||||||
// }
|
// TODO: take settings
|
||||||
// }
|
pub fn new() -> VolMeter {
|
||||||
|
return VolMeter {
|
||||||
|
red: 245,
|
||||||
|
green: 121,
|
||||||
|
blue: 0,
|
||||||
|
x_offset_pct: 10,
|
||||||
|
y_offset_pct: 10,
|
||||||
|
/* dynamic */
|
||||||
|
width: Cell::new(0),
|
||||||
|
row: RefCell::new(vec![]),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: cache input pixbuf?
|
||||||
|
fn meter_draw(
|
||||||
|
&self,
|
||||||
|
volume: i64,
|
||||||
|
pixbuf: &gdk_pixbuf::Pixbuf,
|
||||||
|
) -> Result<gdk_pixbuf::Pixbuf> {
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
pixbuf.get_colorspace() == GDK_COLORSPACE_RGB,
|
||||||
|
"Invalid colorspace in pixbuf"
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
pixbuf.get_bits_per_sample() == 8,
|
||||||
|
"Invalid bits per sample in pixbuf"
|
||||||
|
);
|
||||||
|
ensure!(pixbuf.get_has_alpha(), "No alpha channel in pixbuf");
|
||||||
|
ensure!(
|
||||||
|
pixbuf.get_n_channels() == 4,
|
||||||
|
"Invalid number of channels in pixbuf"
|
||||||
|
);
|
||||||
|
|
||||||
|
let i_width = pixbuf.get_width() as i64;
|
||||||
|
let i_height = pixbuf.get_height() as i64;
|
||||||
|
|
||||||
|
let new_pixbuf = copy_pixbuf(pixbuf);
|
||||||
|
|
||||||
|
let vm_width = i_width / 6;
|
||||||
|
let x = (self.x_offset_pct as f64 *
|
||||||
|
((i_width - vm_width) as f64 / 100.0)) as
|
||||||
|
i64;
|
||||||
|
ensure!(
|
||||||
|
x >= 0 && (x + vm_width) <= i_width,
|
||||||
|
"x coordinate invalid: {}",
|
||||||
|
x
|
||||||
|
);
|
||||||
|
let y = (self.y_offset_pct as f64 * (i_height as f64 / 100.0)) as i64;
|
||||||
|
let vm_height =
|
||||||
|
((i_height - (y * 2)) as f64 * (volume as f64 / 100.0)) as i64;
|
||||||
|
ensure!(
|
||||||
|
y >= 0 && (y + vm_height) <= i_height,
|
||||||
|
"y coordinate invalid: {}",
|
||||||
|
y
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!("vm_height: {}", vm_height);
|
||||||
|
debug!("i_height: {}", i_height);
|
||||||
|
debug!("y: {}", y);
|
||||||
|
debug!("volume: {}", volume);
|
||||||
|
|
||||||
|
/* Let's check if the icon width changed, in which case we
|
||||||
|
* must reinit our internal row of pixels.
|
||||||
|
*/
|
||||||
|
if vm_width != self.width.get() {
|
||||||
|
self.width.set(vm_width);
|
||||||
|
let mut row = self.row.borrow_mut();
|
||||||
|
*row = vec![];
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.row.borrow().len() == 0 {
|
||||||
|
debug!("Allocating vol meter row (width {})", vm_width);
|
||||||
|
let mut row = self.row.borrow_mut();
|
||||||
|
*row = [self.red, self.green, self.blue, 255]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.cycle()
|
||||||
|
.take(vm_width - 1 as usize)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw the volume meter.
|
||||||
|
* Rows in the image are stored top to bottom.
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
let y = i_height - y;
|
||||||
|
let rowstride: i64 = new_pixbuf.get_rowstride() as i64;
|
||||||
|
let pixels: &mut [u8] = unsafe { new_pixbuf.get_pixels() };
|
||||||
|
|
||||||
|
for i in 0..(vm_height - 1) {
|
||||||
|
let row_offset: i64 = y - i;
|
||||||
|
let col_offset: i64 = x * 4;
|
||||||
|
let p_index = ((row_offset * rowstride) + col_offset) as isize;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let p = pixels.as_mut_ptr().offset(p_index) as
|
||||||
|
*mut libc::c_void;
|
||||||
|
memcpy(
|
||||||
|
p,
|
||||||
|
self.row.borrow().as_slice().as_ptr() as
|
||||||
|
*const libc::c_void,
|
||||||
|
self.row.borrow().len(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(new_pixbuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: connect on icon theme change
|
// TODO: connect on icon theme change
|
||||||
@ -63,27 +180,33 @@ struct AudioPix {
|
|||||||
|
|
||||||
|
|
||||||
impl AudioPix {
|
impl AudioPix {
|
||||||
|
// TODO: take settings
|
||||||
pub fn new_from_theme(size: i32) -> Result<AudioPix> {
|
pub fn new_from_theme(size: i32) -> Result<AudioPix> {
|
||||||
let theme: gtk::IconTheme =
|
let theme: gtk::IconTheme = gtk::IconTheme::get_default().ok_or(
|
||||||
gtk::IconTheme::get_default().ok_or("Couldn't get default icon theme")?;
|
"Couldn't get default icon theme",
|
||||||
|
)?;
|
||||||
let pix = AudioPix {
|
let pix = AudioPix {
|
||||||
muted: pixbuf_new_from_theme("audio-volume-muted", size, &theme)?,
|
muted: pixbuf_new_from_theme("audio-volume-muted", size, &theme)?,
|
||||||
low: pixbuf_new_from_theme("audio-volume-low", size, &theme)?,
|
low: pixbuf_new_from_theme("audio-volume-low", size, &theme)?,
|
||||||
medium: pixbuf_new_from_theme("audio-volume-medium", size, &theme)?,
|
medium: pixbuf_new_from_theme("audio-volume-medium", size, &theme)?,
|
||||||
high: pixbuf_new_from_theme("audio-volume-high", size, &theme)?,
|
high: pixbuf_new_from_theme("audio-volume-high", size, &theme)?,
|
||||||
/* 'audio-volume-off' is not available in every icon set.
|
/* 'audio-volume-off' is not available in every icon set.
|
||||||
* Check freedesktop standard for more info:
|
* Check freedesktop standard for more info:
|
||||||
* http://standards.freedesktop.org/icon-naming-spec/
|
* http://standards.freedesktop.org/icon-naming-spec/
|
||||||
* icon-naming-spec-latest.html
|
* icon-naming-spec-latest.html
|
||||||
*/
|
*/
|
||||||
off: pixbuf_new_from_theme("audio-volume-off", size, &theme).or(pixbuf_new_from_theme("audio-volume-low", size, &theme))?,
|
off: pixbuf_new_from_theme("audio-volume-off", size, &theme).or(
|
||||||
|
pixbuf_new_from_theme("audio-volume-low", size, &theme),
|
||||||
|
)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(pix);
|
return Ok(pix);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_pnmixer() -> Result<AudioPix> {
|
pub fn new_from_pnmixer() -> Result<AudioPix> {
|
||||||
gtk::IconTheme::get_default().ok_or("Couldn't get default icon theme")?;
|
gtk::IconTheme::get_default().ok_or(
|
||||||
|
"Couldn't get default icon theme",
|
||||||
|
)?;
|
||||||
let pix = AudioPix {
|
let pix = AudioPix {
|
||||||
muted: pixbuf_new_from_file("pnmixer-muted.png")?,
|
muted: pixbuf_new_from_file("pnmixer-muted.png")?,
|
||||||
low: pixbuf_new_from_file("pnmixer-low.png")?,
|
low: pixbuf_new_from_file("pnmixer-low.png")?,
|
||||||
@ -107,18 +230,21 @@ impl AudioPix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn pixbuf_new_from_theme(icon_name: &str,
|
fn pixbuf_new_from_theme(
|
||||||
size: i32,
|
icon_name: &str,
|
||||||
theme: >k::IconTheme)
|
size: i32,
|
||||||
-> Result<gdk_pixbuf::Pixbuf> {
|
theme: >k::IconTheme,
|
||||||
|
) -> Result<gdk_pixbuf::Pixbuf> {
|
||||||
|
|
||||||
let icon_info =
|
let icon_info = theme
|
||||||
theme.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty())
|
.lookup_icon(icon_name, size, gtk::IconLookupFlags::empty())
|
||||||
.ok_or(format!("Couldn't find icon {}", icon_name))?;
|
.ok_or(format!("Couldn't find icon {}", icon_name))?;
|
||||||
|
|
||||||
debug!("Loading stock icon {} from {:?}",
|
debug!(
|
||||||
icon_name,
|
"Loading stock icon {} from {:?}",
|
||||||
icon_info.get_filename().unwrap_or(PathBuf::new()));
|
icon_name,
|
||||||
|
icon_info.get_filename().unwrap_or(PathBuf::new())
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: propagate error
|
// TODO: propagate error
|
||||||
let pixbuf = icon_info.load_icon().unwrap();
|
let pixbuf = icon_info.load_icon().unwrap();
|
||||||
@ -145,9 +271,15 @@ fn pixbuf_new_from_file(filename: &str) -> Result<gdk_pixbuf::Pixbuf> {
|
|||||||
|
|
||||||
|
|
||||||
fn update_tray_icon(audio_pix: &AudioPix, appstate: &AppS) {
|
fn update_tray_icon(audio_pix: &AudioPix, appstate: &AppS) {
|
||||||
|
let cur_vol = try_w!(appstate.audio.vol());
|
||||||
|
|
||||||
let status_icon = &appstate.gui.status_icon;
|
let status_icon = &appstate.gui.status_icon;
|
||||||
let pixbuf = audio_pix.select_pix(appstate.audio.vol_level());
|
let pixbuf = audio_pix.select_pix(appstate.audio.vol_level());
|
||||||
status_icon.set_from_pixbuf(Some(pixbuf));
|
|
||||||
|
let volmeter = VolMeter::new();
|
||||||
|
let vol_pix = try_w!(volmeter.meter_draw(cur_vol as i64, &pixbuf));
|
||||||
|
|
||||||
|
status_icon.set_from_pixbuf(Some(&vol_pix));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -159,14 +291,14 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
|
|||||||
{
|
{
|
||||||
let _audio_pix = audio_pix.clone();
|
let _audio_pix = audio_pix.clone();
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
appstate.audio.connect_handler(Box::new(move |s, u| {
|
appstate.audio.connect_handler(
|
||||||
match (s, u) {
|
Box::new(move |s, u| match (s, u) {
|
||||||
(AudioSignal::ValuesChanged, _) => {
|
(AudioSignal::ValuesChanged, _) => {
|
||||||
update_tray_icon(&_audio_pix.borrow(), &apps);
|
update_tray_icon(&_audio_pix.borrow(), &apps);
|
||||||
},
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}),
|
||||||
}));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tray_icon.connect_size_changed */
|
/* tray_icon.connect_size_changed */
|
||||||
@ -174,7 +306,9 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
|
|||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let tray_icon = &appstate.gui.status_icon;
|
let tray_icon = &appstate.gui.status_icon;
|
||||||
let _audio_pix = audio_pix.clone();
|
let _audio_pix = audio_pix.clone();
|
||||||
tray_icon.connect_size_changed(move |_, size| on_tray_icon_size_changed(&apps, _audio_pix.as_ref(), size));
|
tray_icon.connect_size_changed(move |_, size| {
|
||||||
|
on_tray_icon_size_changed(&apps, _audio_pix.as_ref(), size)
|
||||||
|
});
|
||||||
tray_icon.set_visible(true);
|
tray_icon.set_visible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,27 +324,27 @@ pub fn init_tray_icon(appstate: Rc<AppS>) {
|
|||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let tray_icon = &appstate.clone().gui.status_icon;
|
let tray_icon = &appstate.clone().gui.status_icon;
|
||||||
tray_icon.connect_scroll_event(move |_, e| {
|
tray_icon.connect_scroll_event(
|
||||||
on_tray_icon_scroll_event(&apps, &e)
|
move |_, e| on_tray_icon_scroll_event(&apps, &e),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tray_icon.connect_popup_menu */
|
/* tray_icon.connect_popup_menu */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let tray_icon = &appstate.clone().gui.status_icon;
|
let tray_icon = &appstate.clone().gui.status_icon;
|
||||||
tray_icon.connect_popup_menu(move |_, _, _| {
|
tray_icon.connect_popup_menu(
|
||||||
on_tray_icon_popup_menu(&apps)
|
move |_, _, _| on_tray_icon_popup_menu(&apps),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* tray_icon.connect_button_release_event */
|
/* tray_icon.connect_button_release_event */
|
||||||
{
|
{
|
||||||
let apps = appstate.clone();
|
let apps = appstate.clone();
|
||||||
let tray_icon = &appstate.clone().gui.status_icon;
|
let tray_icon = &appstate.clone().gui.status_icon;
|
||||||
tray_icon.connect_button_release_event(move |_, eb| {
|
tray_icon.connect_button_release_event(
|
||||||
on_tray_button_release_event(&apps, eb)
|
move |_, eb| on_tray_button_release_event(&apps, eb),
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,9 +369,10 @@ fn on_tray_icon_popup_menu(appstate: &AppS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_tray_icon_scroll_event(appstate: &AppS,
|
fn on_tray_icon_scroll_event(
|
||||||
event: &gdk::EventScroll)
|
appstate: &AppS,
|
||||||
-> bool {
|
event: &gdk::EventScroll,
|
||||||
|
) -> bool {
|
||||||
|
|
||||||
let audio = &appstate.audio;
|
let audio = &appstate.audio;
|
||||||
|
|
||||||
@ -256,10 +391,11 @@ fn on_tray_icon_scroll_event(appstate: &AppS,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_tray_icon_size_changed(appstate: &AppS,
|
fn on_tray_icon_size_changed(
|
||||||
audio_pix: &RefCell<AudioPix>,
|
appstate: &AppS,
|
||||||
size: i32)
|
audio_pix: &RefCell<AudioPix>,
|
||||||
-> bool {
|
size: i32,
|
||||||
|
) -> bool {
|
||||||
debug!("Tray icon size is now {}", size);
|
debug!("Tray icon size is now {}", size);
|
||||||
|
|
||||||
let mut size = size;
|
let mut size = size;
|
||||||
@ -279,9 +415,10 @@ fn on_tray_icon_size_changed(appstate: &AppS,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn on_tray_button_release_event(appstate: &AppS,
|
fn on_tray_button_release_event(
|
||||||
event_button: &gdk::EventButton)
|
appstate: &AppS,
|
||||||
-> bool {
|
event_button: &gdk::EventButton,
|
||||||
|
) -> bool {
|
||||||
let button = event_button.get_button();
|
let button = event_button.get_button();
|
||||||
|
|
||||||
if button != 2 {
|
if button != 2 {
|
||||||
|
Loading…
Reference in New Issue
Block a user