// Copyright (C) 2026 Danilo M. // // 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(); } } }