Stop Bluetooth discovery when the panel closes (#6794)
The discovery retry timer turned adapter.discovering on every second while the panel was open, and nothing ever turned it off. The BlueZ discovery session behind it is held by quickshell's D-Bus connection, so one visit to the panel left the radio in inquiry until the next shell restart — continuously starving A2DP audio on the same controller into stuttering, and 'bluetoothctl show' kept reporting 'Discovering: yes' long after the panel was gone. The panel now tracks the StopDiscovery it owes BlueZ and settles it once closed. A timer bound to the confirmed discovery state does the stopping, rather than a write in the close handler: quickshell only forwards a discovering write that differs from the last state BlueZ reported, so a stop issued while a just-fired StartDiscovery is still awaiting confirmation would be swallowed and leak the session. Binding to adapter.discovering re-arms the stop whenever the confirmation lands, a reopen inside the first interval keeps the scan running uninterrupted, and attempts are bounded so a session another BlueZ client holds up cannot draw StopDiscovery calls forever. One widget instance exists per monitor and they all share the default adapter — the same shared-backend shape the network panel's wifi scanner fix (#6772) dealt with — so the debt follows the session: an instance opening onto a running scan adopts it, a closing instance hands it to a panel still open on another monitor (the popout handoff closes one instance as it opens the next), and a destroyed instance passes it to a surviving sibling. Fixes #6789 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
f075a789f4
commit
457aec6a8c
@@ -20,6 +20,36 @@ assert(/manageIpc: false/.test(panelSource), 'bluetooth owns its IPC handler so
|
||||
assert(/function toggleBluetooth\(\)[\s\S]*?execDetached\(\["omarchy-bluetooth-power", adapter\.enabled \? "off" : "on"\]\)/.test(panelSource), 'bluetooth toggles the radio through the rfkill soft block')
|
||||
assert(!/adapter\.enabled = /.test(panelSource), 'bluetooth never writes the adapter power state directly')
|
||||
|
||||
// Discovery is a BlueZ session that nothing ends at panel close: it persists
|
||||
// until StopDiscovery or until quickshell's D-Bus connection drops with the
|
||||
// shell, and a leaked session keeps the radio in inquiry, starving A2DP audio
|
||||
// on the same controller. The panel tracks the stop it owes and settles it
|
||||
// once closed.
|
||||
const retryTimer = panelSource.match(/id: discoveryRetry[\s\S]*?onTriggered: \{[\s\S]*?\n {4}\}/)
|
||||
assert(retryTimer, 'bluetooth has the discovery retry timer')
|
||||
assert(/owesDiscoveryStop = true/.test(retryTimer[0]), 'bluetooth takes on the stop it owes when it starts discovery')
|
||||
|
||||
// Quickshell only forwards a discovering write that differs from BlueZ's last
|
||||
// confirmed state, so a stop written in the same instant as an in-flight
|
||||
// StartDiscovery would be swallowed. Binding the stop timer to the confirmed
|
||||
// state means a confirmation landing at any point after close re-arms it.
|
||||
const stopTimer = panelSource.match(/id: discoveryStop[\s\S]*?onTriggered: \{[\s\S]*?\n {4}\}/)
|
||||
assert(stopTimer, 'bluetooth has the discovery stop timer')
|
||||
assert(/running: !root\.opened && root\.owesDiscoveryStop[\s\S]*discovering === true/.test(stopTimer[0]), 'bluetooth arms the stop off the confirmed discovery state while closed')
|
||||
assert(/discovering = false/.test(stopTimer[0]), 'bluetooth stops discovery after the panel closes')
|
||||
|
||||
// One widget instance exists per monitor and they share the default adapter,
|
||||
// so a closing instance hands the scan to a panel still open on another
|
||||
// monitor instead of stopping it — that is the popout handoff between
|
||||
// monitors.
|
||||
assert(/function openSibling\(\)/.test(panelSource), 'bluetooth can see panel instances on other monitors')
|
||||
assert(/sibling\.owesDiscoveryStop = true/.test(stopTimer[0]), 'bluetooth moves the stop it owes to an open panel on another monitor instead of stopping its scan')
|
||||
|
||||
// The debt clears when BlueZ confirms discovery down, and a destroyed
|
||||
// instance hands it to a surviving sibling instead of taking it to the grave.
|
||||
assert(/onDiscoveringChanged[\s\S]{0,120}owesDiscoveryStop = false/.test(panelSource), 'bluetooth settles the stop it owes once discovery is confirmed down')
|
||||
assert(/Component\.onDestruction: \{[\s\S]{0,400}owesDiscoveryStop = true[\s\S]{0,200}discovering = false/.test(panelSource), 'bluetooth passes the stop it owes to a sibling when an instance is destroyed')
|
||||
|
||||
assert(bluetooth.isUuidLike('0000110b-0000-1000-8000-00805f9b34fb'), 'bluetooth detects UUID-like names')
|
||||
assert(bluetooth.isAddressLike('AA:BB:CC:DD:EE:FF'), 'bluetooth detects address-like names')
|
||||
assertEqual(bluetooth.normalizedAddress('AA:BB_CC-dd-ee-ff'), 'aabbccddeeff', 'bluetooth normalizes BlueZ and PipeWire address formats')
|
||||
|
||||
Reference in New Issue
Block a user