1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 16:02:51 -05:00

bluetooth: switch to native quickshell bluetooth service

removes dependency on bluetoothctl commands
This commit is contained in:
bbedward
2025-07-15 12:43:22 -04:00
parent 1396b0b582
commit b7da76147f
8 changed files with 1030 additions and 761 deletions

View File

@@ -11,16 +11,12 @@ Singleton {
property var audioSinks: []
property string currentAudioSink: ""
// Microphone properties
property int micLevel: 50
property var audioSources: []
property string currentAudioSource: ""
// Device scanning control
property bool deviceScanningEnabled: false
property bool initialScanComplete: false
// Real Audio Control
Process {
id: volumeChecker
command: ["bash", "-c", "pactl get-sink-volume @DEFAULT_SINK@ | grep -o '[0-9]*%' | head -1 | tr -d '%'"]
@@ -36,7 +32,6 @@ Singleton {
}
}
// Microphone level checker
Process {
id: micLevelChecker
command: ["bash", "-c", "pactl get-source-volume @DEFAULT_SOURCE@ | grep -o '[0-9]*%' | head -1 | tr -d '%'"]
@@ -68,7 +63,6 @@ Singleton {
for (let line of lines) {
line = line.trim()
// New sink starts
if (line.startsWith('Sink #')) {
if (currentSink && currentSink.name && currentSink.id) {
sinks.push(currentSink)
@@ -84,39 +78,31 @@ Singleton {
active: false
}
}
// Get the Name field
else if (line.startsWith('Name: ') && currentSink) {
currentSink.name = line.replace('Name: ', '').trim()
}
// Get the Description field (main display name)
else if (line.startsWith('Description: ') && currentSink) {
currentSink.description = line.replace('Description: ', '').trim()
}
// Get device.description as fallback
else if (line.includes('device.description = ') && currentSink && !currentSink.description) {
currentSink.description = line.replace('device.description = ', '').replace(/"/g, '').trim()
}
// Get node.nick as another fallback option
else if (line.includes('node.nick = ') && currentSink && !currentSink.description) {
currentSink.nick = line.replace('node.nick = ', '').replace(/"/g, '').trim()
}
}
// Add the last sink
if (currentSink && currentSink.name && currentSink.id) {
sinks.push(currentSink)
}
// Process display names
for (let sink of sinks) {
let displayName = sink.description
// If no good description, try nick
if (!displayName || displayName === sink.name) {
displayName = sink.nick
}
// Still no good name? Fall back to smart defaults
if (!displayName || displayName === sink.name) {
if (sink.name.includes("analog-stereo")) displayName = "Built-in Speakers"
else if (sink.name.includes("bluez")) displayName = "Bluetooth Audio"
@@ -136,7 +122,6 @@ Singleton {
}
}
// Audio source (microphone) lister
Process {
id: audioSourceLister
command: ["pactl", "list", "sources"]
@@ -153,7 +138,6 @@ Singleton {
for (let line of lines) {
line = line.trim()
// New source starts
if (line.startsWith('Source #')) {
if (currentSource && currentSource.name && currentSource.id) {
sources.push(currentSource)
@@ -165,23 +149,19 @@ Singleton {
active: false
}
}
// Source name
else if (line.startsWith('Name: ') && currentSource) {
currentSource.name = line.replace('Name: ', '')
}
// Description (display name)
else if (line.startsWith('Description: ') && currentSource) {
let desc = line.replace('Description: ', '')
currentSource.displayName = desc
}
}
// Add the last source
if (currentSource && currentSource.name && currentSource.id) {
sources.push(currentSource)
}
// Filter out monitor sources (we want actual input devices)
sources = sources.filter(source => !source.name.includes('.monitor'))
root.audioSources = sources
@@ -202,7 +182,6 @@ Singleton {
if (data.trim()) {
root.currentAudioSink = data.trim()
// Update active status in audioSinks
let updatedSinks = []
for (let sink of root.audioSinks) {
updatedSinks.push({
@@ -218,7 +197,6 @@ Singleton {
}
}
// Default source (microphone) checker
Process {
id: defaultSourceChecker
command: ["pactl", "get-default-source"]
@@ -230,7 +208,6 @@ Singleton {
if (data.trim()) {
root.currentAudioSource = data.trim()
// Update active status in audioSources
let updatedSources = []
for (let source of root.audioSources) {
updatedSources.push({
@@ -271,12 +248,10 @@ Singleton {
function setAudioSink(sinkName) {
console.log("Setting audio sink to:", sinkName)
// Use a more reliable approach instead of Qt.createQmlObject
sinkSetProcess.command = ["pactl", "set-default-sink", sinkName]
sinkSetProcess.running = true
}
// Dedicated process for setting audio sink
Process {
id: sinkSetProcess
running: false
@@ -285,7 +260,6 @@ Singleton {
console.log("Audio sink change exit code:", exitCode)
if (exitCode === 0) {
console.log("Audio sink changed successfully")
// Refresh current sink and list
defaultSinkChecker.running = true
if (root.deviceScanningEnabled) {
audioSinkLister.running = true
@@ -303,7 +277,6 @@ Singleton {
sourceSetProcess.running = true
}
// Dedicated process for setting audio source
Process {
id: sourceSetProcess
running: false
@@ -312,7 +285,6 @@ Singleton {
console.log("Audio source change exit code:", exitCode)
if (exitCode === 0) {
console.log("Audio source changed successfully")
// Refresh current source and list
defaultSourceChecker.running = true
if (root.deviceScanningEnabled) {
audioSourceLister.running = true
@@ -337,25 +309,21 @@ Singleton {
Component.onCompleted: {
console.log("AudioService: Starting initialization...")
// Do initial device scan
audioSinkLister.running = true
audioSourceLister.running = true
initialScanComplete = true
console.log("AudioService: Initialization complete")
}
// Control functions for managing device scanning
function enableDeviceScanning(enabled) {
console.log("AudioService: Device scanning", enabled ? "enabled" : "disabled")
root.deviceScanningEnabled = enabled
if (enabled && root.initialScanComplete) {
// Immediately scan when enabled
audioSinkLister.running = true
audioSourceLister.running = true
}
}
// Manual refresh function for when user opens audio settings
function refreshDevices() {
console.log("AudioService: Manual device refresh triggered")
audioSinkLister.running = true