From 50d5ba42f57ce77fa09bf582d217c4b9cb90365a Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Mon, 4 Aug 2025 12:30:48 +0545 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(osd):=20improve?= =?UTF-8?q?=20audio=20and=20microphone=20volume/mute=20handling=20with=20'?= =?UTF-8?q?changed'=20signal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/osd.py | 327 +++++++++++++++++++++++++------------------------ styles/osd.css | 6 +- 2 files changed, 169 insertions(+), 164 deletions(-) diff --git a/modules/osd.py b/modules/osd.py index ce0b94e..a39cd1d 100644 --- a/modules/osd.py +++ b/modules/osd.py @@ -129,16 +129,40 @@ class AudioOSDContainer(Box): self.scale.connect("value-changed", self.on_volume_changed) self.audio.connect("notify::speaker", self.on_audio_speaker_changed) - - # Connect to speaker-changed signal directly self.audio.connect("speaker-changed", self.on_speaker_changed) # Connect to current speaker if available + self._connect_speaker_signals() + + def _connect_speaker_signals(self): + """Connect to speaker's changed signal which should fire on both volume and mute changes""" if self.audio.speaker: - self.audio.speaker.connect( - "notify::volume", self.on_volume_property_changed + # Connect to the main 'changed' signal from AudioStream + self.audio.speaker.connect("changed", self.on_speaker_stream_changed) + + def on_speaker_stream_changed(self, *_): + """This should be called whenever the speaker stream changes (volume OR mute)""" + if self.audio.speaker: + current_volume = ( + round(self.audio.speaker.volume) + if hasattr(self.audio.speaker, "volume") + else 0 ) - self.audio.speaker.connect("notify::muted", self.on_mute_changed) + current_muted = ( + self.audio.speaker.muted + if hasattr(self.audio.speaker, "muted") + else False + ) + + # Check if either volume or mute state changed + if ( + self.previous_volume != current_volume + or self.previous_muted != current_muted + ): + self.previous_volume = current_volume + self.previous_muted = current_muted + self.update_volume() + self.emit("volume-changed") def get_svg(self, value): audio_level = 0 if value == 0 else min(int(math.ceil(value / 33)), 3) @@ -146,92 +170,75 @@ class AudioOSDContainer(Box): def sync_with_audio(self): if self.audio.speaker: - volume = round(self.audio.speaker.volume) + volume = ( + round(self.audio.speaker.volume) + if hasattr(self.audio.speaker, "volume") + else 0 + ) self.scale.set_value(volume) - # self.update_icon(volume) self.previous_volume = volume - self.previous_muted = self.audio.speaker.muted + self.previous_muted = ( + self.audio.speaker.muted + if hasattr(self.audio.speaker, "muted") + else False + ) def on_volume_changed(self, *_): if self.audio.speaker: volume = self.scale.value if 0 <= volume <= 100: self.audio.speaker.set_volume(volume) - - if volume == 0 or (self.audio.speaker and self.audio.speaker.muted): - self.scale.add_style_class("muted") - volume = 0 - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/volume/audio-volume-{ - self.get_svg(volume) - }.svg" - ) - ) - - else: - self.on_volume_property_changed() - self.scale.remove_style_class("muted") - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/volume/audio-volume-{ - self.get_svg(volume) - }.svg" - ) - ) - # self.icon.remove_style_class("muted") - + self.update_volume_display(volume) self.emit("volume-changed") + def update_volume_display(self, volume=None): + """Update the visual display based on current volume/mute state""" + if not self.audio.speaker: + return + + if volume is None: + volume = ( + round(self.audio.speaker.volume) + if hasattr(self.audio.speaker, "volume") + else 0 + ) + + is_muted = ( + self.audio.speaker.muted if hasattr(self.audio.speaker, "muted") else False + ) + + if volume == 0 or is_muted: + self.scale.add_style_class("muted") + display_volume = 0 + else: + self.scale.remove_style_class("muted") + display_volume = volume + + self.osd_window_image.set_from_file( + get_relative_path( + f"../config/assets/icons/volume/audio-volume-{ + self.get_svg(display_volume) + }.svg" + ) + ) + def on_audio_speaker_changed(self, *_): - if self.audio.speaker: - self.audio.speaker.connect( - "notify::volume", self.on_volume_property_changed - ) - self.audio.speaker.connect("notify::muted", self.on_mute_changed) - self.update_volume() - - def on_speaker_changed(self, *_): + self._connect_speaker_signals() self.update_volume() - def on_volume_property_changed(self, *_): - if self.audio.speaker: - current_volume = round(self.audio.speaker.volume) - if self.previous_volume is None or current_volume != self.previous_volume: - self.previous_volume = current_volume - self.update_volume() - self.emit("volume-changed") + def on_speaker_changed(self, *_): + self._connect_speaker_signals() + self.update_volume() def update_volume(self, *_): if self.audio.speaker and not self.is_hovered(): - volume = round(self.audio.speaker.volume) + volume = ( + round(self.audio.speaker.volume) + if hasattr(self.audio.speaker, "volume") + else 0 + ) self.scale.set_value(volume) - if volume == 0 or (self.audio.speaker and self.audio.speaker.muted): - self.scale.add_style_class("muted") - volume = 0 - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/volume/audio-volume-{ - self.get_svg(volume) - }.svg" - ) - ) - else: - self.scale.remove_style_class("muted") - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/volume/audio-volume-{ - self.get_svg(volume) - }.svg" - ) - ) - - def on_mute_changed(self, *_): - if self.audio.speaker: - current_muted = self.audio.speaker.muted - if self.previous_muted is None or current_muted != self.previous_muted: - self.previous_muted = current_muted - self.emit("volume-changed") + self.update_volume_display(volume) class MicrophoneOSDContainer(Box): @@ -269,15 +276,40 @@ class MicrophoneOSDContainer(Box): self.scale.connect("value-changed", self.on_volume_changed) self.audio.connect("notify::microphone", self.on_audio_microphone_changed) - - # Connect to microphone-changed signal directly self.audio.connect("microphone-changed", self.on_microphone_changed) + # Connect to current microphone if available + self._connect_microphone_signals() + + def _connect_microphone_signals(self): + """Connect to microphone's changed signal which should fire on both volume and mute changes""" if self.audio.microphone: - self.audio.microphone.connect( - "notify::volume", self.on_volume_property_changed + # Connect to the main 'changed' signal from AudioStream + self.audio.microphone.connect("changed", self.on_microphone_stream_changed) + + def on_microphone_stream_changed(self, *_): + """This should be called whenever the microphone stream changes (volume OR mute)""" + if self.audio.microphone: + current_volume = ( + round(self.audio.microphone.volume) + if hasattr(self.audio.microphone, "volume") + else 0 ) - self.audio.microphone.connect("notify::muted", self.on_mute_changed) + current_muted = ( + self.audio.microphone.muted + if hasattr(self.audio.microphone, "muted") + else False + ) + + # Check if either volume or mute state changed + if ( + self.previous_volume != current_volume + or self.previous_muted != current_muted + ): + self.previous_volume = current_volume + self.previous_muted = current_muted + self.update_volume() + self.emit("mic-changed") def get_svg(self, value): audio_level = 0 if value == 0 else min(int(math.ceil(value / 33)), 3) @@ -285,105 +317,77 @@ class MicrophoneOSDContainer(Box): def sync_with_audio(self): if self.audio.microphone: - volume = round(self.audio.microphone.volume) + volume = ( + round(self.audio.microphone.volume) + if hasattr(self.audio.microphone, "volume") + else 0 + ) self.scale.set_value(volume) self.previous_volume = volume - self.previous_muted = self.audio.microphone.muted + self.previous_muted = ( + self.audio.microphone.muted + if hasattr(self.audio.microphone, "muted") + else False + ) def on_volume_changed(self, *_): if self.audio.microphone: volume = self.scale.value if 0 <= volume <= 100: self.audio.microphone.set_volume(volume) - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(volume) - }.svg" - ) - ) - - if volume == 0 or ( - self.audio.microphone and self.audio.microphone.muted - ): - volume = 0 - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(volume) - }.svg" - ) - ) - - else: - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(volume) - }.svg" - ) - ) - self.scale.remove_style_class("muted") - + self.update_volume_display(volume) self.emit("mic-changed") + def update_volume_display(self, volume=None): + """Update the visual display based on current volume/mute state""" + if not self.audio.microphone: + return + + if volume is None: + volume = ( + round(self.audio.microphone.volume) + if hasattr(self.audio.microphone, "volume") + else 0 + ) + + is_muted = ( + self.audio.microphone.muted + if hasattr(self.audio.microphone, "muted") + else False + ) + + if volume == 0 or is_muted: + self.scale.add_style_class("muted") + display_volume = 0 + else: + self.scale.remove_style_class("muted") + display_volume = volume + + self.osd_window_image.set_from_file( + get_relative_path( + f"../config/assets/icons/mic/microphone-{ + self.get_svg(display_volume) + }.svg" + ) + ) + def on_audio_microphone_changed(self, *_): - if self.audio.microphone: - self.audio.microphone.connect( - "notify::volume", self.on_volume_property_changed - ) - self.audio.microphone.connect("notify::muted", self.on_mute_changed) - self.update_volume() - - def on_microphone_changed(self, *_): + self._connect_microphone_signals() self.update_volume() - def on_volume_property_changed(self, *_): - if self.audio.microphone: - current_volume = round(self.audio.microphone.volume) - if self.previous_volume is None or current_volume != self.previous_volume: - self.previous_volume = current_volume - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(current_volume) - }.svg" - ) - ) - self.update_volume() - self.emit("mic-changed") + def on_microphone_changed(self, *_): + self._connect_microphone_signals() + self.update_volume() def update_volume(self, *_): if self.audio.microphone and not self.is_hovered(): - volume = round(self.audio.microphone.volume) + volume = ( + round(self.audio.microphone.volume) + if hasattr(self.audio.microphone, "volume") + else 0 + ) self.scale.set_value(volume) - - if volume == 0 or (self.audio.microphone and self.audio.microphone.muted): - self.scale.add_style_class("muted") - volume = 0 - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(volume) - }.svg" - ) - ) - else: - self.osd_window_image.set_from_file( - get_relative_path( - f"../config/assets/icons/mic/microphone-{ - self.get_svg(volume) - }.svg" - ) - ) - self.scale.remove_style_class("muted") - - def on_mute_changed(self, *_): - if self.audio.microphone: - current_muted = self.audio.microphone.muted - if self.previous_muted is None or current_muted != self.previous_muted: - self.previous_muted = current_muted - GLib.idle_add(lambda: self.emit("mic-changed")) + self.update_volume_display(volume) class OSD(Window): @@ -419,6 +423,7 @@ class OSD(Window): self.last_activity_time = time.time() + # Connect to the containers' signals self.audio_container.connect("volume-changed", self.show_audio) self.brightness_container.brightness_service.connect( "screen", self.show_brightness diff --git a/styles/osd.css b/styles/osd.css index 7b13501..c4c2082 100644 --- a/styles/osd.css +++ b/styles/osd.css @@ -1,8 +1,8 @@ #osd { - background-color: alpha(#fff, 0.09); + background-color: alpha(#fff, 0.09); padding: 12px 20px; margin: 70px; - min-height: 200px; + min-height: 200px; border-radius: 16px; } @@ -19,7 +19,7 @@ } #osd trough highlight { - border-radius: 100px 0px 0px 100px; + border-radius: 100px; background: var(--primary); transition: all 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.275); }