// Output / input device picker. Writing Pipewire.preferredDefaultAudioSink is // what actually moves audio; the default* properties are read-only reflections // of what the session manager settled on. import QtQuick import Quickshell import Quickshell.Services.Pipewire import qs.config Item { id: root // false → input devices. property bool output: true property alias maxHeight: list.maxHeight implicitHeight: list.implicitHeight readonly property var nodes: { return Pipewire.nodes.values.filter(n => { if (n.isStream) return false; // Sources have to be filtered on the type flags: !isSink also // matches video nodes (webcams show up here otherwise). return root.output ? n.isSink : (n.type & PwNodeType.AudioSource) === PwNodeType.AudioSource; }); } readonly property var current: root.output ? Pipewire.defaultAudioSink : Pipewire.defaultAudioSource function select(node): void { if (root.output) Pipewire.preferredDefaultAudioSink = node; else Pipewire.preferredDefaultAudioSource = node; } ScrollColumn { id: list anchors.fill: parent maxHeight: 220 Repeater { model: root.nodes RowButton { id: nodeRow required property var modelData width: parent.width implicitHeight: 38 icon: root.output ? "audio-speakers-symbolic" : "audio-input-microphone-symbolic" iconFallback: "audio-card-symbolic" label: nodeRow.modelData.description || nodeRow.modelData.nickname || nodeRow.modelData.name selected: nodeRow.modelData === root.current onClicked: root.select(nodeRow.modelData) } } } }