Files

82 lines
2.8 KiB
QML

pragma Singleton
// Shared PipeWire device discovery and default selection. Quick Settings and
// Panama Settings intentionally use this same boundary so they cannot disagree
// about what counts as an input or which node should become the default.
import Quickshell
import Quickshell.Services.Pipewire
import QtQuick
import "AudioStreams.js" as AudioStreams
Singleton {
id: root
readonly property var outputs: Pipewire.nodes.values.filter(node =>
!node.isStream && node.isSink)
readonly property var inputs: Pipewire.nodes.values.filter(node =>
!node.isStream
&& (node.type & PwNodeType.AudioSource) === PwNodeType.AudioSource)
readonly property var playbackStreams: Pipewire.nodes.values.filter(node =>
node.ready && node.audio
&& (node.type & PwNodeType.AudioOutStream) === PwNodeType.AudioOutStream)
readonly property var applications: AudioStreams.group(
root.playbackStreams, PwNodeType.AudioOutStream)
// The capture side of the same story: which applications currently hold the
// microphone open. Same grouping rules as playback, so an application that
// records on three streams reads as one entry with one mute.
readonly property var captureStreams: Pipewire.nodes.values.filter(node =>
node.ready && node.audio
&& (node.type & PwNodeType.AudioInStream) === PwNodeType.AudioInStream)
readonly property var captureApplications: AudioStreams.group(
root.captureStreams, PwNodeType.AudioInStream)
function nodes(output: bool): var {
return output ? root.outputs : root.inputs;
}
function current(output: bool): var {
return output ? Pipewire.defaultAudioSink : Pipewire.defaultAudioSource;
}
function select(output: bool, node: var): void {
if (!node)
return;
if (output)
Pipewire.preferredDefaultAudioSink = node;
else
Pipewire.preferredDefaultAudioSource = node;
}
function label(node: var): string {
if (!node)
return "Unknown device";
return node.description || node.nickname || node.name || "Unknown device";
}
function applicationVolume(application: var): real {
return AudioStreams.volume(application);
}
function applicationMuted(application: var): bool {
return AudioStreams.muted(application);
}
// `max` is the clamp ceiling; omitting it means 1, and anything that is not
// a positive number is treated as omitted. Callers that honor the
// over-amplification setting pass 1.5.
function setApplicationVolume(application: var, value: real, max: real): bool {
return AudioStreams.setVolume(application, value, max);
}
function setApplicationMuted(application: var, muted: bool): bool {
return AudioStreams.setMuted(application, muted);
}
}