82 lines
2.8 KiB
QML
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);
|
|
}
|
|
}
|