feat(audio): update volume sliders when changing volume with media keys

This commit is contained in:
Jason Hansen 2024-07-08 08:20:28 -06:00 committed by GitHub
parent b4d280c477
commit a630428f08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 134 additions and 33 deletions

View file

@ -66,10 +66,6 @@ pub struct Audio {
current_input: Option<DeviceInfo>,
outputs: Vec<DeviceInfo>,
inputs: Vec<DeviceInfo>,
sink_mute: bool,
sink_volume: u32,
source_mute: bool,
source_volume: u32,
pulse_state: PulseState,
popup: Option<window::Id>,
timeline: Timeline,
@ -84,13 +80,15 @@ impl Audio {
}
fn output_icon_name(&self) -> &'static str {
if self.sink_mute || self.sink_volume == 0 {
let volume = self.current_output_volume_percent();
let mute = self.current_output_mute();
if mute || volume == 0. {
"audio-volume-muted-symbolic"
} else if self.sink_volume < 33 {
} else if volume < 33. {
"audio-volume-low-symbolic"
} else if self.sink_volume < 66 {
} else if volume < 66. {
"audio-volume-medium-symbolic"
} else if self.sink_volume <= 100 {
} else if volume <= 100. {
"audio-volume-high-symbolic"
} else {
"audio-volume-overamplified-symbolic"
@ -102,11 +100,13 @@ impl Audio {
}
fn input_icon_name(&self) -> &'static str {
if self.source_mute || self.source_volume == 0 {
let volume = self.current_input_volume_percent();
let mute = self.current_input_mute();
if mute || volume == 0. {
"microphone-sensitivity-muted-symbolic"
} else if self.source_volume < 33 {
} else if volume < 33. {
"microphone-sensitivity-low-symbolic"
} else if self.source_volume < 66 {
} else if volume < 66. {
"microphone-sensitivity-medium-symbolic"
} else {
"microphone-sensitivity-high-symbolic"
@ -126,6 +126,8 @@ pub enum Message {
Ignore,
SetOutputVolume(f64),
SetInputVolume(f64),
SetOutputMute(bool),
SetInputMute(bool),
OutputToggle,
InputToggle,
OutputChanged(String),
@ -253,6 +255,38 @@ impl Audio {
}
})
}
fn current_output_volume_percent(&self) -> f64 {
volume_to_percent(
self.current_output
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
}
fn current_input_volume_percent(&self) -> f64 {
volume_to_percent(
self.current_input
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
)
}
fn current_output_mute(&self) -> bool {
self.current_output
.as_ref()
.map(|o| o.mute)
.unwrap_or_default()
}
fn current_input_mute(&self) -> bool {
self.current_input
.as_ref()
.map(|o| o.mute)
.unwrap_or_default()
}
}
impl cosmic::Application for Audio {
@ -358,6 +392,34 @@ impl cosmic::Application for Audio {
}
}
}
Message::SetOutputMute(mute) => {
if let Some(output) = self.current_output.as_mut() {
output.mute = mute;
}
if let PulseState::Connected(connection) = &mut self.pulse_state {
if let Some(device) = &self.current_output {
if let Some(name) = &device.name {
connection
.send(pulse::Message::SetSinkMuteByName(name.clone(), device.mute))
}
}
}
}
Message::SetInputMute(mute) => {
if let Some(input) = self.current_input.as_mut() {
input.mute = mute;
}
if let PulseState::Connected(connection) = &mut self.pulse_state {
if let Some(device) = &self.current_input {
if let Some(name) = &device.name {
connection.send(pulse::Message::SetSourceMuteByName(
name.clone(),
device.mute,
))
}
}
}
}
Message::OutputChanged(val) => {
if let Some(conn) = self.pulse_state.connection() {
if let Some(val) = self.outputs.iter().find(|o| o.name.as_ref() == Some(&val)) {
@ -539,16 +601,28 @@ impl cosmic::Application for Audio {
},
Message::PulseSub(event) => match event {
sub_pulse::Event::SinkVolume(value) => {
self.sink_volume = value;
self.current_output.as_mut().map(|output| {
output
.volume
.set(output.volume.len(), percent_to_volume(value as f64))
});
}
sub_pulse::Event::SinkMute(value) => {
self.sink_mute = value;
if let Some(output) = self.current_output.as_mut() {
output.mute = value;
}
}
sub_pulse::Event::SourceVolume(value) => {
self.source_volume = value;
self.current_input.as_mut().map(|input| {
input
.volume
.set(input.volume.len(), percent_to_volume(value as f64))
});
}
sub_pulse::Event::SourceMute(value) => {
self.source_mute = value;
if let Some(input) = self.current_input.as_mut() {
input.mute = value;
}
}
},
};
@ -615,18 +689,10 @@ impl cosmic::Application for Audio {
fn view_window(&self, _id: window::Id) -> Element<Message> {
let audio_disabled = matches!(self.pulse_state, PulseState::Disconnected(_));
let out_f64 = volume_to_percent(
self.current_output
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
);
let in_f64 = volume_to_percent(
self.current_input
.as_ref()
.map(|o| o.volume.avg())
.unwrap_or_default(),
);
let out_f64 = self.current_output_volume_percent();
let in_f64 = self.current_input_volume_percent();
let out_mute = self.current_output_mute();
let in_mute = self.current_input_mute();
let mut audio_content = if audio_disabled {
column![padded_control(
@ -639,9 +705,15 @@ impl cosmic::Application for Audio {
column![
padded_control(
row![
icon::from_name(self.output_icon_name())
.size(24)
.symbolic(true),
button::icon(
icon::from_name(self.output_icon_name())
.size(24)
.symbolic(true),
)
.style(cosmic::theme::Button::Icon)
.icon_size(24)
.line_height(24)
.on_press(Message::SetOutputMute(!out_mute)),
slider(0.0..=100.0, out_f64, Message::SetOutputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", out_f64.round()))
@ -654,9 +726,15 @@ impl cosmic::Application for Audio {
),
padded_control(
row![
icon::from_name(self.input_icon_name())
.size(24)
.symbolic(true),
button::icon(
icon::from_name(self.input_icon_name())
.size(24)
.symbolic(true),
)
.style(cosmic::theme::Button::Icon)
.icon_size(24)
.line_height(24)
.on_press(Message::SetInputMute(!in_mute)),
slider(0.0..=100.0, in_f64, Message::SetInputVolume)
.width(Length::FillPortion(5)),
text(format!("{}%", in_f64.round()))

View file

@ -173,6 +173,8 @@ pub enum Message {
SetDefaultSource(DeviceInfo),
SetSinkVolumeByName(String, ChannelVolumes),
SetSourceVolumeByName(String, ChannelVolumes),
SetSinkMuteByName(String, bool),
SetSourceMuteByName(String, bool),
}
struct PulseHandle {
@ -290,6 +292,27 @@ impl PulseHandle {
};
server.set_source_volume_by_name(&name, &channel_volumes)
}
Message::SetSinkMuteByName(name, mute) => {
let server = match server.as_mut() {
Some(s) => s,
None => continue,
};
let op =
server.introspector.set_sink_mute_by_name(&name, mute, None);
server.wait_for_result(op).ok();
}
Message::SetSourceMuteByName(name, mute) => {
let server = match server.as_mut() {
Some(s) => s,
None => continue,
};
let op = server
.introspector
.set_source_mute_by_name(&name, mute, None);
server.wait_for_result(op).ok();
}
Message::UpdateConnection => {
tracing::info!(
"Updating Connection, server exists: {:?}",