From 089dc98166e1a0636a097b1bd446dac63b968c3f Mon Sep 17 00:00:00 2001 From: bbedward Date: Tue, 6 Oct 2026 15:02:12 -0400 Subject: [PATCH] network: set routing preference only from settings fixes #3068 --- core/internal/server/network/priority.go | 23 ++++++----- core/internal/server/network/priority_test.go | 12 ++++-- .../ControlCenter/Details/NetworkDetail.qml | 40 +++++++++---------- .../Modules/Settings/NetworkStatusTab.qml | 2 +- quickshell/Services/DMSNetworkService.qml | 26 ++++-------- quickshell/Services/NetworkService.qml | 12 ------ 6 files changed, 48 insertions(+), 67 deletions(-) diff --git a/core/internal/server/network/priority.go b/core/internal/server/network/priority.go index 612131f61..b53dc96a2 100644 --- a/core/internal/server/network/priority.go +++ b/core/internal/server/network/priority.go @@ -16,7 +16,8 @@ const ( metricPreferred = int64(100) metricNonPreferred = int64(300) - metricDefault = int64(100) + // -1 lets NetworkManager pick the metric by device type (nm-settings ipv4.route-metric) + metricDefault = int64(-1) ) func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error { @@ -46,7 +47,7 @@ func (m *Manager) SetConnectionPreference(pref ConnectionPreference) error { case PreferenceCellular: return m.prioritizeCellular() case PreferenceAuto: - return m.balancePriorities() + return m.restoreDefaultPriorities() } return nil @@ -112,7 +113,7 @@ func (m *Manager) prioritizeCellular() error { return nil } -func (m *Manager) balancePriorities() error { +func (m *Manager) restoreDefaultPriorities() error { if err := m.setConnectionPriority("802-3-ethernet", priorityDefault, metricDefault); err != nil { log.Warnf("Failed to reset Ethernet priority: %v", err) } @@ -242,16 +243,20 @@ func (m *Manager) setConnectionPriority(connType string, autoconnectPriority int } func priorityMatches(variant dbus.Variant, expected int64) bool { - value, ok := variantInt64(variant) - return ok && value == expected + return settingInt64(variant, int64(priorityDefault)) == expected } func routeMetricMatches(section map[string]dbus.Variant, expected int64) bool { - if section == nil { - return false + return settingInt64(section["route-metric"], metricDefault) == expected +} + +// GetSettings omits properties at their default, so a missing key is the default value +func settingInt64(variant dbus.Variant, def int64) int64 { + value, ok := variantInt64(variant) + if !ok { + return def } - value, ok := variantInt64(section["route-metric"]) - return ok && value == expected + return value } func variantInt64(variant dbus.Variant) (int64, bool) { diff --git a/core/internal/server/network/priority_test.go b/core/internal/server/network/priority_test.go index f332ec619..e8651658f 100644 --- a/core/internal/server/network/priority_test.go +++ b/core/internal/server/network/priority_test.go @@ -3,6 +3,7 @@ package network import ( "testing" + "github.com/godbus/dbus/v5" "github.com/stretchr/testify/assert" ) @@ -20,7 +21,10 @@ func TestManager_SetConnectionPreference(t *testing.T) { }) } -// Note: Full testing of priority operations would require mocking NetworkManager -// D-Bus interfaces. The tests above cover the basic logic and error handling. -// Integration tests would be needed for complete coverage of network connection -// priority updates and reactivation. +func TestProfileAtDefaultsMatchesAutoTargets(t *testing.T) { + assert.True(t, priorityMatches(dbus.Variant{}, int64(priorityDefault))) + assert.True(t, routeMetricMatches(nil, metricDefault)) + assert.True(t, routeMetricMatches(map[string]dbus.Variant{"method": dbus.MakeVariant("auto")}, metricDefault)) + assert.False(t, routeMetricMatches(map[string]dbus.Variant{"route-metric": dbus.MakeVariant(int64(100))}, metricDefault)) + assert.False(t, priorityMatches(dbus.MakeVariant(int32(100)), int64(priorityDefault))) +} diff --git a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml index e1dad712f..efc63ccda 100644 --- a/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml +++ b/quickshell/Modules/ControlCenter/Details/NetworkDetail.qml @@ -52,7 +52,7 @@ Item { readonly property string currentConnectionType: { if (selectedType && connectionTypes.includes(selectedType)) return selectedType; - return connectionTypes[Math.max(0, currentPreferenceIndex)] || "wifi"; + return connectionTypes[defaultTypeIndex] || "wifi"; } readonly property bool wifiMode: currentConnectionType === "wifi" readonly property bool ethernetMode: currentConnectionType === "ethernet" @@ -66,18 +66,21 @@ Item { readonly property bool wifiScanningEmpty: wifiMode && NetworkService.wifiEnabled && !NetworkService.wifiToggling && NetworkService.wifiInterface && (NetworkService.wifiNetworks?.length ?? 0) < 1 && (NetworkService.isScanning || transitioning) readonly property bool wifiListVisible: wifiMode && NetworkService.wifiEnabled && !NetworkService.wifiToggling && !wifiScanningEmpty - readonly property int currentPreferenceIndex: { - if (DMSService.apiVersion < 5) - return 1; - if (!networkManager || DMSService.apiVersion <= 10) - return 1; - const pref = NetworkService.userPreference; - if (connectionTypes.indexOf(pref) !== -1) - return connectionTypes.indexOf(pref); - if (connectionTypes.indexOf(NetworkService.networkStatus) !== -1) - return connectionTypes.indexOf(NetworkService.networkStatus); - const wifiIndex = connectionTypes.indexOf("wifi"); - return wifiIndex !== -1 ? wifiIndex : 0; + readonly property bool paneSwitchSupported: connectionTypes.length > 1 && networkManager && DMSService.apiVersion > 10 + readonly property var typeLabels: ({ + "ethernet": I18n.tr("Ethernet"), + "wifi": I18n.tr("Wi-Fi", "wireless network, page and section title"), + "cellular": I18n.tr("Cellular") + }) + readonly property int defaultTypeIndex: { + const wifiIndex = Math.max(0, connectionTypes.indexOf("wifi")); + if (!paneSwitchSupported) + return wifiIndex; + const byStatus = connectionTypes.indexOf(NetworkService.networkStatus); + if (byStatus !== -1) + return byStatus; + const byPreference = connectionTypes.indexOf(NetworkService.userPreference); + return byPreference !== -1 ? byPreference : wifiIndex; } readonly property Item headerActions: Row { @@ -318,23 +321,16 @@ Item { bottomPadding: pageList.count > 0 ? CcMetrics.detailContentGap : 0 DButtonGroup { - readonly property var labelsByType: ({ - "ethernet": I18n.tr("Ethernet"), - "wifi": I18n.tr("Wi-Fi", "wireless network, page and section title"), - "cellular": I18n.tr("Cellular") - }) - anchors.horizontalCenter: parent.horizontalCenter size: "small" - visible: root.connectionTypes.length > 1 && root.networkManager && DMSService.apiVersion > 10 - model: root.connectionTypes.map(t => labelsByType[t] || t) + visible: root.paneSwitchSupported + model: root.connectionTypes.map(t => root.typeLabels[t] || t) currentIndex: Math.max(0, root.connectionTypes.indexOf(root.currentConnectionType)) selectionMode: "single" onSelectionChanged: (index, selected) => { if (!selected) return; root.selectedType = root.connectionTypes[index] || "wifi"; - NetworkService.setNetworkPreference(root.selectedType); } } diff --git a/quickshell/Modules/Settings/NetworkStatusTab.qml b/quickshell/Modules/Settings/NetworkStatusTab.qml index a1965cc65..1b7d69e8a 100644 --- a/quickshell/Modules/Settings/NetworkStatusTab.qml +++ b/quickshell/Modules/Settings/NetworkStatusTab.qml @@ -122,7 +122,7 @@ Item { Row { width: parent.width spacing: Theme.spacingM - visible: NetworkService.backend === "networkmanager" && [NetworkService.ethernetConnected, NetworkService.wifiConnected, NetworkService.cellularConnected].filter(v => v).length > 1 + visible: NetworkService.backend === "networkmanager" && [NetworkService.ethernetDevices, NetworkService.wifiDevices, NetworkService.cellularDevices].filter(d => (d?.length ?? 0) > 0).length > 1 StyledText { text: I18n.tr("Preference", "noun, which network connection type is preferred") diff --git a/quickshell/Services/DMSNetworkService.qml b/quickshell/Services/DMSNetworkService.qml index 02b92c58d..6cc9a7bf7 100644 --- a/quickshell/Services/DMSNetworkService.qml +++ b/quickshell/Services/DMSNetworkService.qml @@ -537,8 +537,8 @@ Singleton { log.info("Successfully connected to", pendingConnectionSSID, "in", elapsed, "ms"); ToastService.showInfo(I18n.tr("Connected to %1", "wifi toast, %1 is the network name").arg(pendingConnectionSSID)); - if (userPreference === "wifi" || userPreference === "auto") { - setConnectionPriority("wifi"); + if (userPreference === "wifi") { + applyNetworkPreference("wifi"); } pendingConnectionSSID = ""; @@ -864,10 +864,13 @@ Singleton { function setNetworkPreference(preference) { if (!networkAvailable) return; + SettingsData.set("networkPreference", preference); + applyNetworkPreference(preference); + } + + function applyNetworkPreference(preference) { changingPreference = true; targetPreference = preference; - SettingsData.set("networkPreference", preference); - DMSService.sendRequest("network.preference.set", { preference: preference }, response => { @@ -880,21 +883,6 @@ Singleton { }); } - function setConnectionPriority(type) { - if (type === "wifi") { - setNetworkPreference("wifi"); - } else if (type === "ethernet") { - setNetworkPreference("ethernet"); - } else if (type === "cellular") { - setNetworkPreference("cellular"); - } - } - - function connectToWifiAndSetPreference(ssid, password, username = "", anonymousIdentity = "", domainSuffixMatch = "", hidden = false, eapMethod = "", phase2Auth = "") { - connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch, hidden, eapMethod, phase2Auth); - setNetworkPreference("wifi"); - } - function toggleNetworkConnection(type) { if (!networkAvailable) return; diff --git a/quickshell/Services/NetworkService.qml b/quickshell/Services/NetworkService.qml index fe59d7739..7c7b7f3a6 100644 --- a/quickshell/Services/NetworkService.qml +++ b/quickshell/Services/NetworkService.qml @@ -253,18 +253,6 @@ Singleton { } } - function setConnectionPriority(type) { - if (activeService && activeService.setConnectionPriority) { - activeService.setConnectionPriority(type); - } - } - - function connectToWifiAndSetPreference(ssid, password, username = "", anonymousIdentity = "", domainSuffixMatch = "", hidden = false, eapMethod = "", phase2Auth = "") { - if (activeService && activeService.connectToWifiAndSetPreference) { - activeService.connectToWifiAndSetPreference(ssid, password, username, anonymousIdentity, domainSuffixMatch, hidden, eapMethod, phase2Auth); - } - } - function toggleNetworkConnection(type) { if (activeService && activeService.toggleNetworkConnection) { activeService.toggleNetworkConnection(type);