import Quickshell import Quickshell.Io import Quickshell.Services.Pipewire import QtQuick import "services/AudioStreams.js" as AudioStreams import qs.services ShellRoot { readonly property int audioOutStreamFlag: 4 property var fixtureNodes: [ { id: 10, ready: true, type: audioOutStreamFlag, properties: { "application.id": "org.chromium.Chromium", "application.name": "Chromium", "application.icon_name": "chromium" }, description: "Chromium audio", audio: { volume: 0.4, muted: false } }, { id: 11, ready: true, type: audioOutStreamFlag, properties: { "application.id": "org.chromium.Chromium", "application.name": "Chromium", "application.icon_name": "chromium" }, description: "Chromium audio", audio: { volume: 0.8, muted: true } }, { id: 20, ready: true, type: audioOutStreamFlag, properties: { "application.process.binary": "spotify", "application.name": "Spotify" }, description: "Spotify", audio: { volume: 0.25, muted: false } }, { id: 30, ready: true, type: 2, properties: { "application.name": "Microphone capture" }, description: "Input stream", audio: { volume: 0.5, muted: false } }, { id: 40, ready: false, type: audioOutStreamFlag, properties: { "application.name": "Not ready" }, description: "Unready stream", audio: { volume: 0.5, muted: false } }, { id: 99, ready: true, type: audioOutStreamFlag, properties: {}, description: "", audio: { volume: 1, muted: false } } ] function groups(): var { return AudioStreams.group(fixtureNodes, audioOutStreamFlag); } // A throwaway two-stream group for the clamp cases. The shared fixture // above is mutated by the volume and mute tests, and a clamp assertion that // depended on which of those ran first would be worthless. function clampFixture(): var { return { key: "clamp", label: "Clamp", icon: "audio-x-generic-symbolic", nodes: [ { audio: { volume: 0.1, muted: true } }, { audio: { volume: 0.1, muted: true } } ] }; } IpcHandler { target: "application-volume-test" function summary(): string { const applications = groups(); const chromium = applications.find(application => application.key === "org.chromium.Chromium"); return JSON.stringify({ groups: applications.map(application => ({ key: application.key, label: application.label, icon: application.icon, count: application.nodes.length })), chromiumVolume: AudioStreams.volume(chromium), chromiumMuted: AudioStreams.muted(chromium) }); } function mutateVolume(): string { const chromium = groups().find(application => application.key === "org.chromium.Chromium"); const changed = AudioStreams.setVolume(chromium, 0.7); return JSON.stringify({ changed, volumes: chromium.nodes.map(node => node.audio.volume), muted: chromium.nodes.map(node => node.audio.muted) }); } function mutateMute(): string { const chromium = groups().find(application => application.key === "org.chromium.Chromium"); const changed = AudioStreams.setMuted(chromium, true); return JSON.stringify({ changed, muted: chromium.nodes.map(node => node.audio.muted) }); } // Over-amplification is a preference, so the ceiling is an argument // rather than a constant -- this file stays Settings-free on purpose. function clampVolume(): string { const overAmp = clampFixture(); const overAmpChanged = AudioStreams.setVolume(overAmp, 1.4, 1.5); const ceiling = clampFixture(); AudioStreams.setVolume(ceiling, 2.5, 1.5); const defaultMax = clampFixture(); AudioStreams.setVolume(defaultMax, 1.4); const floor = clampFixture(); AudioStreams.setVolume(floor, -0.5, 1.5); const nonNumeric = clampFixture(); const nonNumericChanged = AudioStreams.setVolume(nonNumeric, "loud", 1.5); return JSON.stringify({ overAmpChanged, overAmp: overAmp.nodes.map(node => node.audio.volume), overAmpMuted: overAmp.nodes.map(node => node.audio.muted), ceiling: ceiling.nodes.map(node => node.audio.volume), defaultMax: defaultMax.nodes.map(node => node.audio.volume), floor: floor.nodes.map(node => node.audio.volume), nonNumericChanged, nonNumeric: nonNumeric.nodes.map(node => node.audio.volume) }); } function serviceSummary(): string { const applications = AudioDevices.applications; return JSON.stringify({ count: applications.length, validTypes: applications.every(application => application.nodes.every(node => (node.type & PwNodeType.AudioOutStream) === PwNodeType.AudioOutStream)) }); } function invalidMutations(): string { return JSON.stringify({ nullVolume: AudioDevices.setApplicationVolume(null, 0.5), emptyMute: AudioDevices.setApplicationMuted({ nodes: [] }, true) }); } } }