audio: Set icon based on volume and mute status of output

This commit is contained in:
Ian Douglas Scott 2022-07-22 14:44:12 -07:00
parent 3884f08504
commit 0e7baf704b
3 changed files with 51 additions and 29 deletions

View file

@ -82,9 +82,8 @@ fn app(application: &Application) {
set_application: Some(application),
set_title: Some("COSMIC Network Applet"),
#[wrap(Some)]
set_child = &libcosmic_applet::AppletButton {
// TODO: adjust based on volume, mute
set_button_icon_name: "multimedia-volume-control-symbolic",
set_child: button = &libcosmic_applet::AppletButton {
set_button_icon_name: "audio-volume-medium-symbolic",
#[wrap(Some)]
set_popover_child: window_box = &GtkBox {
set_orientation: Orientation::Vertical,
@ -180,11 +179,26 @@ fn app(application: &Application) {
}),
);
glib::MainContext::default().spawn_local(
clone!(@weak outputs, @weak current_output, @weak output_volume, @strong pa => async move {
clone!(@weak outputs, @weak current_output, @weak output_volume, @strong pa, @strong button, => async move {
while let Some(()) = refresh_output_rx.next().await {
output::refresh_output_widgets(&pa, &outputs);
let default_output = output::refresh_default_output(&pa, &current_output).await;
volume::update_volume(&default_output, &output_volume);
button.set_button_icon_name({
let volume = default_output.volume.avg().0 as f64 / Volume::NORMAL.0 as f64;
// XXX correct cutoffs?
if default_output.mute {
"audio-volume-muted"
} else if volume > 1.0 {
"audio-volume-overamplified-symbolic"
} else if volume > 0.66 {
"audio-volume-high-symbolic"
} else if volume > 0.33 {
"audio-volume-medium-symbolic"
} else {
"audio-volume-low-symbolic"
}
});
}
}),
);

View file

@ -2,7 +2,7 @@ use gtk4::glib;
use libpulse_binding::{
callbacks::ListResult,
context::{
introspect::{Introspector, SinkInfo},
introspect::{Introspector, SinkInfo, SourceInfo},
subscribe::{Facility, InterestMaskSet, Operation},
Context, FlagSet, State,
},
@ -19,9 +19,34 @@ pub struct DeviceInfo {
pub name: Option<String>,
pub description: Option<String>,
pub volume: ChannelVolumes,
pub mute: bool,
pub index: u32,
}
impl<'a> From<&SinkInfo<'a>> for DeviceInfo {
fn from(info: &SinkInfo<'a>) -> Self {
Self {
name: info.name.clone().map(|x| x.into_owned()),
description: info.description.clone().map(|x| x.into_owned()),
volume: info.volume,
mute: info.mute,
index: info.index,
}
}
}
impl<'a> From<&SourceInfo<'a>> for DeviceInfo {
fn from(info: &SourceInfo<'a>) -> Self {
Self {
name: info.name.clone().map(|x| x.into_owned()),
description: info.description.clone().map(|x| x.into_owned()),
volume: info.volume,
mute: info.mute,
index: info.index,
}
}
}
pub struct ServerInfo {
pub default_sink_name: Option<String>,
pub default_source_name: Option<String>,
@ -105,12 +130,7 @@ impl PA {
PAFut::new(|waker| {
self.introspect()
.get_sink_info_list(move |result| match result {
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo {
name: item.name.clone().map(|x| x.into_owned()),
description: item.description.clone().map(|x| x.into_owned()),
volume: item.volume,
index: item.index,
}),
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo::from(item)),
ListResult::End => waker.wake(Ok(items.take().unwrap())),
ListResult::Error => waker.wake(Err(())),
})
@ -130,12 +150,7 @@ impl PA {
self.introspect()
.get_sink_info_by_name(&name, move |result| match result {
ListResult::Item(item) => {
sink = Some(DeviceInfo {
name: item.name.clone().map(|x| x.into_owned()),
description: item.description.clone().map(|x| x.into_owned()),
volume: item.volume,
index: item.index,
});
sink = Some(DeviceInfo::from(item));
}
ListResult::End => waker.wake(sink.take().ok_or(())),
ListResult::Error => waker.wake(Err(())),
@ -158,12 +173,7 @@ impl PA {
PAFut::new(|waker| {
self.introspect()
.get_source_info_list(move |result| match result {
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo {
name: item.name.clone().map(|x| x.into_owned()),
description: item.description.clone().map(|x| x.into_owned()),
volume: item.volume,
index: item.index,
}),
ListResult::Item(item) => items.as_mut().unwrap().push(DeviceInfo::from(item)),
ListResult::End => waker.wake(Ok(items.take().unwrap())),
ListResult::Error => waker.wake(Err(())),
})
@ -183,12 +193,7 @@ impl PA {
self.introspect()
.get_source_info_by_name(&name, move |result| match result {
ListResult::Item(item) => {
source = Some(DeviceInfo {
name: item.name.clone().map(|x| x.into_owned()),
description: item.description.clone().map(|x| x.into_owned()),
volume: item.volume,
index: item.index,
});
source = Some(DeviceInfo::from(item));
}
ListResult::End => waker.wake(source.take().ok_or(())),
ListResult::Error => waker.wake(Err(())),

View file

@ -1,3 +1,6 @@
// TODO: Use `Volume::ui_max()`?
// * Make sure volumes greater than this are handled properly.
use gtk4::{glib, prelude::*, subclass::prelude::*};
use libpulse_binding::volume::{ChannelVolumes, Volume};
use std::{