Tier 0: render what the services already decided, honestly

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-25 00:24:30 -04:00
parent be0e55214b
commit 8b1205e4b8
38 changed files with 1474 additions and 260 deletions
@@ -21,9 +21,65 @@ import QtQuick
Singleton {
id: root
// Set by the page while it is visible; drives both scanners.
// Set by the settings page while it is visible. One of several holds on
// the radios rather than the only one -- see the scan holds below.
property bool active: false
// ── Scan holds ───────────────────────────────────────────────────────────
//
// Two surfaces list the same radios: this page and the quick-settings
// panel. Both used to write scannerEnabled and adapter.discovering from
// their own visibility flag, and the last writer won -- so closing the
// settings page turned scanning off underneath an open quick-settings
// panel, which then sat on "Searching…" forever with nothing searching.
//
// So the radios are held rather than switched. Each surface acquires a
// named hold while it is on screen and releases it when it goes away, and
// scanning runs while ANY hold is outstanding. Names rather than a counter:
// these holders are QML items that can be destroyed with a release already
// in flight, and a counter that drifts either leaves the radio on forever
// or turns it off under somebody. Acquiring a name twice is acquiring it
// once.
property var wifiScanHolders: []
property var discoveryHolders: []
readonly property bool wifiScanWanted: root.wifiScanHolders.length > 0
readonly property bool discoveryWanted: root.discoveryHolders.length > 0
// Whether it was this service that started BlueZ discovering. Something
// else on the machine may already have been -- bluetoothctl, another
// desktop component -- and stopping a scan we did not start is not ours to
// do.
property bool discoveryOwned: false
function acquireWifiScan(holder: string): void {
if (holder === "" || root.wifiScanHolders.indexOf(holder) >= 0)
return;
root.wifiScanHolders = root.wifiScanHolders.concat([holder]);
root.syncScanners();
}
function releaseWifiScan(holder: string): void {
if (root.wifiScanHolders.indexOf(holder) < 0)
return;
root.wifiScanHolders = root.wifiScanHolders.filter(name => name !== holder);
root.syncScanners();
}
function acquireDiscovery(holder: string): void {
if (holder === "" || root.discoveryHolders.indexOf(holder) >= 0)
return;
root.discoveryHolders = root.discoveryHolders.concat([holder]);
root.syncScanners();
}
function releaseDiscovery(holder: string): void {
if (root.discoveryHolders.indexOf(holder) < 0)
return;
root.discoveryHolders = root.discoveryHolders.filter(name => name !== holder);
root.syncScanners();
}
readonly property var wifiDevice: {
for (const device of Networking.devices.values) {
if (device.type === DeviceType.Wifi)
@@ -213,20 +269,39 @@ Singleton {
return "Could not connect";
}
// Scanning follows visibility. NetworkManager keeps scanning as long as it
// is asked to, and Bluetooth discovery is worse -- it holds the radio.
// Scanning follows the outstanding holds, not any one surface's visibility.
// NetworkManager keeps scanning as long as it is asked to, and Bluetooth
// discovery is worse -- it holds the radio.
//
// Computed from state and applied idempotently, so calling this after every
// acquire, release and device change is free: it writes only when the radio
// is not already where the holds say it should be.
function syncScanners(): void {
if (root.wifiDevice)
root.wifiDevice.scannerEnabled = root.active && root.wifiEnabled;
root.wifiDevice.scannerEnabled = root.wifiScanWanted && root.wifiEnabled;
if (root.adapter && root.adapter.enabled) {
const shouldDiscover = root.active;
if (root.adapter.discovering !== shouldDiscover)
root.adapter.discovering = shouldDiscover;
if (!root.adapter || !root.adapter.enabled)
return;
const shouldDiscover = root.discoveryWanted;
if (shouldDiscover && !root.adapter.discovering) {
root.adapter.discovering = true;
root.discoveryOwned = true;
} else if (!shouldDiscover && root.discoveryOwned && root.adapter.discovering) {
root.adapter.discovering = false;
root.discoveryOwned = false;
}
}
onActiveChanged: root.syncScanners()
// The settings page's own hold, expressed through the flag it already sets.
onActiveChanged: {
if (root.active) {
root.acquireWifiScan("settings");
root.acquireDiscovery("settings");
} else {
root.releaseWifiScan("settings");
root.releaseDiscovery("settings");
}
}
onWifiDeviceChanged: root.syncScanners()
onWifiEnabledChanged: root.syncScanners()
onAdapterChanged: root.syncScanners()