audio: Set icon based on volume and mute status of output
This commit is contained in:
parent
3884f08504
commit
0e7baf704b
3 changed files with 51 additions and 29 deletions
|
|
@ -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, ¤t_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"
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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(())),
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue