1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00

audio: add IPC & OSD for changing output audio device

fixes #754
This commit is contained in:
bbedward
2025-12-03 20:47:57 -05:00
parent 62bd6e41ef
commit 6c7776a9a6
7 changed files with 132 additions and 0 deletions

View File

@@ -59,6 +59,7 @@ const DMS_ACTIONS = [
{ id: "spawn dms ipc call audio decrement 10", label: "Volume Down (10%)" },
{ id: "spawn dms ipc call audio mute", label: "Volume Mute Toggle" },
{ id: "spawn dms ipc call audio micmute", label: "Microphone Mute Toggle" },
{ id: "spawn dms ipc call audio cycleoutput", label: "Audio Output: Cycle" },
{ id: "spawn dms ipc call brightness increment", label: "Brightness Up" },
{ id: "spawn dms ipc call brightness increment 1", label: "Brightness Up (1%)" },
{ id: "spawn dms ipc call brightness increment 5", label: "Brightness Up (5%)" },

View File

@@ -313,6 +313,7 @@ Singleton {
property bool osdMicMuteEnabled: true
property bool osdCapsLockEnabled: true
property bool osdPowerProfileEnabled: true
property bool osdAudioOutputEnabled: true
property bool powerActionConfirm: true
property int powerActionHoldDuration: 1

View File

@@ -212,6 +212,7 @@ var SPEC = {
osdMicMuteEnabled: { def: true },
osdCapsLockEnabled: { def: true },
osdPowerProfileEnabled: { def: false },
osdAudioOutputEnabled: { def: true },
powerActionConfirm: { def: true },
powerActionHoldDuration: { def: 1 },

View File

@@ -700,6 +700,14 @@ Item {
}
}
Variants {
model: SettingsData.getFilteredScreens("osd")
delegate: AudioOutputOSD {
modelData: item
}
}
LazyLoader {
id: hyprlandOverviewLoader
active: CompositorService.isHyprland

View File

@@ -0,0 +1,80 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
DankOSD {
id: root
property string deviceName: ""
property string deviceIcon: "speaker"
osdWidth: Math.min(Math.max(120, Theme.iconSize + textMetrics.width + Theme.spacingS * 4), Screen.width - Theme.spacingM * 2)
osdHeight: 40 + Theme.spacingS * 2
autoHideInterval: 2500
enableMouseInteraction: false
TextMetrics {
id: textMetrics
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
font.family: Theme.fontFamily
text: root.deviceName
}
function getIconForSink(sink) {
if (!sink)
return "speaker";
const name = sink.name || "";
if (name.includes("bluez"))
return "headset";
if (name.includes("hdmi"))
return "tv";
if (name.includes("usb"))
return "headset";
return "speaker";
}
Connections {
target: AudioService
function onAudioOutputCycled(name) {
if (!SettingsData.osdAudioOutputEnabled)
return;
root.deviceName = name;
root.deviceIcon = getIconForSink(AudioService.sink);
root.show();
}
}
content: Item {
property int gap: Theme.spacingS
anchors.centerIn: parent
width: parent.width - Theme.spacingS * 2
height: 40
DankIcon {
id: iconItem
width: Theme.iconSize
height: Theme.iconSize
x: parent.gap
anchors.verticalCenter: parent.verticalCenter
name: root.deviceIcon
size: Theme.iconSize
color: Theme.primary
}
StyledText {
id: textItem
x: parent.gap * 2 + Theme.iconSize
width: parent.width - Theme.iconSize - parent.gap * 3
anchors.verticalCenter: parent.verticalCenter
text: root.deviceName
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
elide: Text.ElideRight
}
}
}

View File

@@ -141,6 +141,13 @@ Item {
checked: SettingsData.osdPowerProfileEnabled
onToggled: checked => SettingsData.set("osdPowerProfileEnabled", checked)
}
SettingsToggleRow {
text: I18n.tr("Audio Output Switch")
description: I18n.tr("Show on-screen display when cycling audio output devices")
checked: SettingsData.osdAudioOutputEnabled
onToggled: checked => SettingsData.set("osdAudioOutputEnabled", checked)
}
}
}
}

View File

@@ -30,6 +30,33 @@ Singleton {
property var mediaDevicesConnections: null
signal micMuteChanged
signal audioOutputCycled(string deviceName)
function getAvailableSinks() {
return Pipewire.nodes.values.filter(node => node.audio && node.isSink && !node.isStream);
}
function cycleAudioOutput() {
const sinks = getAvailableSinks();
if (sinks.length < 2)
return null;
const currentSink = root.sink;
let currentIndex = -1;
for (let i = 0; i < sinks.length; i++) {
if (sinks[i] === currentSink) {
currentIndex = i;
break;
}
}
const nextIndex = (currentIndex + 1) % sinks.length;
const nextSink = sinks[nextIndex];
Pipewire.preferredDefaultAudioSink = nextSink;
const name = displayName(nextSink);
audioOutputCycled(name);
return name;
}
Connections {
target: root.sink?.audio ?? null
@@ -595,6 +622,13 @@ Singleton {
return result;
}
function cycleoutput(): string {
const result = root.cycleAudioOutput();
if (!result)
return "Only one audio output available";
return `Switched to: ${result}`;
}
}
Connections {