diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-14 12:24:13 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-14 12:24:13 +0200 |
| commit | d203d938f005d7542bf13e217ac51917866a8ace (patch) | |
| tree | c4a6d8728461099638a3fc3378ba7fb34a2b5874 /desktop/modules/sound/Service.qml | |
| parent | 55619e7dc830697d2929b0bd5cf1c6dc7435720c (diff) | |
| download | quickshell-d203d938f005d7542bf13e217ac51917866a8ace.tar.gz quickshell-d203d938f005d7542bf13e217ac51917866a8ace.zip | |
feat(desktop): move volume-osd in as the sound module
VolumeOsd.qml did three jobs in one file: PipeWire tracking, the OSD
surface and the player transport. They become Service, Osd and the page.
The OSD keeps its namespace so the existing Hyprland blur rule still
matches, and the service stays always-active because the OSD has to
answer a keypress with no drawer open.
Diffstat (limited to 'desktop/modules/sound/Service.qml')
| -rw-r--r-- | desktop/modules/sound/Service.qml | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/desktop/modules/sound/Service.qml b/desktop/modules/sound/Service.qml new file mode 100644 index 0000000..e509a94 --- /dev/null +++ b/desktop/modules/sound/Service.qml @@ -0,0 +1,75 @@ +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +import Quickshell +import Quickshell.Services.Pipewire +import QtQuick + +// The PipeWire half of what used to be VolumeOsd.qml. Always active: the OSD +// has to react to a volume keypress with no drawer open. +Scope { + id: root + + readonly property PwNode sink: Pipewire.defaultAudioSink + readonly property PwNode source: Pipewire.defaultAudioSource + + readonly property real volume: sink?.audio?.volume ?? 0 + readonly property bool muted: sink?.audio?.muted ?? false + + // Which node changed last, and whether it was the input. The OSD draws + // this one; null means nothing to show. + property PwNode active: null + property bool isInput: false + + // Keeping the nodes bound is what makes volume/muted actually update. + // Without the tracker the value reads once and goes stale. + PwObjectTracker { objects: [root.sink, root.source].filter(n => n !== null) } + + signal changed() + + // A node reports its initial volume while binding, before `ready` goes + // true, so the `ready` check alone suppresses the startup values. Nothing + // else may be swallowed: the next signal after that is the user's first + // keypress, and eating it costs the OSD its first appearance. + function show(node, input) { + if (!node?.ready || !node.audio) return; + root.active = node; + root.isInput = input; + root.changed(); + } + + function showTrack() { + if (!Player.active) return; + root.active = root.sink; + root.isInput = false; + root.changed(); + } + + Connections { + target: root.sink?.audio ?? null + function onVolumeChanged() { root.show(root.sink, false); } + function onMutedChanged() { root.show(root.sink, false); } + } + + Connections { + target: root.source?.audio ?? null + function onVolumeChanged() { root.show(root.source, true); } + function onMutedChanged() { root.show(root.source, true); } + } + + // A track change shows the OSD as well, so the row is not something you + // only see when you happen to touch the volume. + Connections { + target: Player.current ?? null + function onTrackTitleChanged() { if (Player.title) root.showTrack(); } + function onPlaybackStateChanged() { root.showTrack(); } + } +} |
