import QtQuick import Quickshell.Io // service to get the current volume and handle volume changes Item { id: root signal volumeChanged(int volume) // process to get the volume level: first active playback stream, falling back to the sink Process { id: audioVolumeProcess command: ["sh", "-c", "pactl list sink-inputs | grep -m1 'Volume:' | grep -oP '\\d+(?=%)' | head -1 || wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{printf \"%d\", $2 * 100}'"] stdout: StdioCollector { onStreamFinished: { if (this.text) { const volume = parseInt(this.text.trim()); root.volumeChanged(volume); } } } } Process { id: volumeUpProcess command: ["sh", "-c", "pactl list sink-inputs | awk '/^Sink Input #/{sub(/^Sink Input #/,x);id=$1;got=0} /^[[:space:]]+Volume:/ && id && !got{match($0,/[0-9]+%/);if(substr($0,RSTART,RLENGTH-1)+0<100)print id;got=1}' | xargs -r -I{} pactl set-sink-input-volume {} +1%"] } Process { id: volumeDownProcess command: ["sh", "-c", "pactl list short sink-inputs | awk '{print $1}' | xargs -r -I{} pactl set-sink-input-volume {} -1%"] } function volumeUp() { volumeUpProcess.running = true; audioVolumeProcess.running = true; } function volumeDown() { volumeDownProcess.running = true; audioVolumeProcess.running = true; } // timer to periodically check the volume level Timer { interval: 5000 running: true repeat: true onTriggered: audioVolumeProcess.running = true } Component.onCompleted: audioVolumeProcess.running = true }