mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-05-12 07:19:41 -04:00
- Remove all compat code - Rewire LegacyNetworkService to use Quickshell.Networking - Add parentWindow to settings child windows
623 lines
18 KiB
QML
623 lines
18 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import Quickshell.Networking
|
|
import qs.Common
|
|
import qs.Services
|
|
|
|
Singleton {
|
|
id: root
|
|
readonly property var log: Log.scoped("LegacyNetworkService")
|
|
|
|
property bool isActive: false
|
|
|
|
readonly property string backend: Networking.backend === NetworkBackendType.NetworkManager ? "networkmanager" : ""
|
|
readonly property string primaryConnection: ""
|
|
|
|
readonly property var allDevices: Networking.devices?.values ?? []
|
|
|
|
readonly property var wifiDevices: allDevices.filter(d => d.type === DeviceType.Wifi).map(d => ({
|
|
"name": d.name
|
|
}))
|
|
readonly property var ethernetDevices: allDevices.filter(d => d.type === DeviceType.Wired).map(d => ({
|
|
"name": d.name
|
|
}))
|
|
|
|
property string wifiDeviceOverride: SessionData.wifiDeviceOverride || ""
|
|
|
|
readonly property var wifiDevice: {
|
|
const list = allDevices.filter(d => d.type === DeviceType.Wifi);
|
|
if (wifiDeviceOverride) {
|
|
const match = list.find(d => d.name === wifiDeviceOverride);
|
|
if (match) {
|
|
return match;
|
|
}
|
|
}
|
|
return list[0] ?? null;
|
|
}
|
|
|
|
readonly property var wiredDevice: allDevices.find(d => d.type === DeviceType.Wired) ?? null
|
|
|
|
readonly property bool ethernetConnected: wiredDevice?.connected ?? false
|
|
readonly property string ethernetInterface: wiredDevice?.name ?? ""
|
|
property string ethernetIP: ""
|
|
readonly property string ethernetConnectionUuid: {
|
|
const net = wiredDevice?.network;
|
|
if (!net) {
|
|
return "";
|
|
}
|
|
const settings = net.nmSettings;
|
|
return settings.length > 0 ? settings[0].uuid : "";
|
|
}
|
|
readonly property var wiredConnections: []
|
|
|
|
readonly property bool wifiAvailable: wifiDevice !== null
|
|
readonly property bool wifiEnabled: Networking.wifiEnabled
|
|
readonly property string wifiInterface: wifiDevice?.name ?? ""
|
|
readonly property bool wifiConnected: wifiDevice?.connected ?? false
|
|
property string wifiIP: ""
|
|
readonly property string wifiDevicePath: wifiDevice?.name ?? ""
|
|
readonly property string activeAccessPointPath: ""
|
|
readonly property string connectingDevice: ""
|
|
|
|
readonly property var connectedWifiNetwork: {
|
|
const dev = wifiDevice;
|
|
if (!dev) {
|
|
return null;
|
|
}
|
|
const list = dev.networks?.values ?? [];
|
|
for (const net of list) {
|
|
if (net.connected) {
|
|
return net;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
readonly property string currentWifiSSID: connectedWifiNetwork?.name ?? ""
|
|
readonly property int wifiSignalStrength: Math.round((connectedWifiNetwork?.signalStrength ?? 0) * 100)
|
|
readonly property string wifiConnectionUuid: {
|
|
const net = connectedWifiNetwork;
|
|
if (!net) {
|
|
return "";
|
|
}
|
|
const settings = net.nmSettings;
|
|
return settings.length > 0 ? settings[0].uuid : "";
|
|
}
|
|
|
|
readonly property string networkStatus: {
|
|
if (ethernetConnected) {
|
|
return "ethernet";
|
|
}
|
|
if (wifiConnected) {
|
|
return "wifi";
|
|
}
|
|
return "disconnected";
|
|
}
|
|
|
|
readonly property string wifiSignalIcon: {
|
|
if (!wifiConnected || networkStatus !== "wifi") {
|
|
return "wifi_off";
|
|
}
|
|
if (wifiSignalStrength >= 50) {
|
|
return "wifi";
|
|
}
|
|
if (wifiSignalStrength >= 25) {
|
|
return "wifi_2_bar";
|
|
}
|
|
return "wifi_1_bar";
|
|
}
|
|
|
|
readonly property var wifiNetworks: {
|
|
const dev = wifiDevice;
|
|
if (!dev) {
|
|
return [];
|
|
}
|
|
const list = dev.networks?.values ?? [];
|
|
const result = [];
|
|
const seen = new Set();
|
|
for (const net of list) {
|
|
if (!net?.name || seen.has(net.name)) {
|
|
continue;
|
|
}
|
|
seen.add(net.name);
|
|
result.push({
|
|
"ssid": net.name,
|
|
"signal": Math.round(net.signalStrength * 100),
|
|
"secured": net.security !== WifiSecurityType.Open,
|
|
"bssid": "",
|
|
"connected": net.connected,
|
|
"saved": net.known
|
|
});
|
|
}
|
|
result.sort((a, b) => b.signal - a.signal);
|
|
return result;
|
|
}
|
|
|
|
readonly property var savedConnections: wifiNetworks.filter(n => n.saved).map(n => ({
|
|
"ssid": n.ssid,
|
|
"saved": true
|
|
}))
|
|
readonly property var savedWifiNetworks: savedConnections
|
|
readonly property var ssidToConnectionName: {
|
|
const map = {};
|
|
for (const n of wifiNetworks) {
|
|
if (n.saved) {
|
|
map[n.ssid] = n.ssid;
|
|
}
|
|
}
|
|
return map;
|
|
}
|
|
|
|
property string userPreference: "auto"
|
|
property bool isConnecting: false
|
|
property string connectingSSID: ""
|
|
property string connectionError: ""
|
|
property string lastConnectionError: ""
|
|
property string connectionStatus: ""
|
|
property bool passwordDialogShouldReopen: false
|
|
|
|
readonly property bool isScanning: wifiDevice?.scannerEnabled ?? false
|
|
property bool autoScan: false
|
|
property bool autoRefreshEnabled: false
|
|
property bool wifiToggling: false
|
|
property bool changingPreference: false
|
|
property string targetPreference: ""
|
|
property string wifiPassword: ""
|
|
property string forgetSSID: ""
|
|
property int refCount: 0
|
|
|
|
property string networkInfoSSID: ""
|
|
property string networkInfoDetails: ""
|
|
property bool networkInfoLoading: false
|
|
property string networkWiredInfoUUID: ""
|
|
property string networkWiredInfoDetails: ""
|
|
property bool networkWiredInfoLoading: false
|
|
|
|
signal networksUpdated
|
|
signal connectionChanged
|
|
|
|
readonly property var lowPriorityCmd: ["nice", "-n", "19", "ionice", "-c3"]
|
|
|
|
property var _pendingNetwork: null
|
|
property string _pendingSSID: ""
|
|
property bool _pendingWithPsk: false
|
|
|
|
onWifiNetworksChanged: networksUpdated()
|
|
onNetworkStatusChanged: {
|
|
connectionChanged();
|
|
refreshIPs();
|
|
}
|
|
onCurrentWifiSSIDChanged: {
|
|
connectionChanged();
|
|
refreshIPs();
|
|
}
|
|
onEthernetInterfaceChanged: refreshIPs()
|
|
onWifiInterfaceChanged: refreshIPs()
|
|
onWifiEnabledChanged: {
|
|
if (wifiEnabled && autoScan && wifiDevice) {
|
|
wifiDevice.scannerEnabled = true;
|
|
}
|
|
if (wifiToggling) {
|
|
wifiToggling = false;
|
|
ToastService.showInfo(wifiEnabled ? I18n.tr("WiFi enabled") : I18n.tr("WiFi disabled"));
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
userPreference = SettingsData.networkPreference;
|
|
}
|
|
|
|
Connections {
|
|
target: root._pendingNetwork
|
|
enabled: root._pendingNetwork !== null
|
|
|
|
function onConnectionFailed(reason) {
|
|
root._handleConnectionFailed(reason);
|
|
}
|
|
|
|
function onConnectedChanged() {
|
|
if (root._pendingNetwork?.connected) {
|
|
root._handleConnectionSuccess();
|
|
}
|
|
}
|
|
}
|
|
|
|
function activate() {
|
|
if (isActive) {
|
|
return;
|
|
}
|
|
isActive = true;
|
|
log.info("Activating...");
|
|
refreshIPs();
|
|
if (wifiDevice && wifiEnabled) {
|
|
wifiDevice.scannerEnabled = true;
|
|
}
|
|
}
|
|
|
|
function addRef() {
|
|
refCount++;
|
|
if (refCount === 1) {
|
|
startAutoScan();
|
|
}
|
|
}
|
|
|
|
function removeRef() {
|
|
refCount = Math.max(0, refCount - 1);
|
|
if (refCount === 0) {
|
|
stopAutoScan();
|
|
}
|
|
}
|
|
|
|
function startAutoScan() {
|
|
autoScan = true;
|
|
autoRefreshEnabled = true;
|
|
if (wifiDevice && wifiEnabled) {
|
|
wifiDevice.scannerEnabled = true;
|
|
}
|
|
}
|
|
|
|
function stopAutoScan() {
|
|
autoScan = false;
|
|
autoRefreshEnabled = false;
|
|
if (wifiDevice) {
|
|
wifiDevice.scannerEnabled = false;
|
|
}
|
|
}
|
|
|
|
function scanWifi() {
|
|
if (!wifiDevice || !wifiEnabled) {
|
|
return;
|
|
}
|
|
wifiDevice.scannerEnabled = true;
|
|
}
|
|
|
|
function scanWifiNetworks() {
|
|
scanWifi();
|
|
}
|
|
|
|
function _findNetworkBySSID(ssid) {
|
|
const dev = wifiDevice;
|
|
if (!dev) {
|
|
return null;
|
|
}
|
|
return dev.networks?.values?.find(n => n.name === ssid) ?? null;
|
|
}
|
|
|
|
function _handleConnectionFailed(reason) {
|
|
const ssid = _pendingSSID;
|
|
let invalidPsk = false;
|
|
|
|
switch (reason) {
|
|
case ConnectionFailReason.NoSecrets:
|
|
invalidPsk = _pendingWithPsk;
|
|
break;
|
|
case ConnectionFailReason.WifiAuthTimeout:
|
|
invalidPsk = true;
|
|
break;
|
|
}
|
|
|
|
connectionStatus = invalidPsk ? "invalid_password" : "failed";
|
|
connectionError = ConnectionFailReason.toString(reason);
|
|
lastConnectionError = connectionError;
|
|
passwordDialogShouldReopen = invalidPsk;
|
|
isConnecting = false;
|
|
connectingSSID = "";
|
|
|
|
if (invalidPsk) {
|
|
ToastService.showError(I18n.tr("Invalid password for %1").arg(ssid));
|
|
} else {
|
|
ToastService.showError(I18n.tr("Failed to connect to %1").arg(ssid));
|
|
}
|
|
|
|
_pendingNetwork = null;
|
|
_pendingSSID = "";
|
|
_pendingWithPsk = false;
|
|
}
|
|
|
|
function _handleConnectionSuccess() {
|
|
const ssid = _pendingSSID;
|
|
connectionStatus = "connected";
|
|
connectionError = "";
|
|
isConnecting = false;
|
|
connectingSSID = "";
|
|
passwordDialogShouldReopen = false;
|
|
|
|
ToastService.showInfo(I18n.tr("Connected to %1").arg(ssid));
|
|
|
|
if (userPreference === "wifi" || userPreference === "auto") {
|
|
setConnectionPriority("wifi");
|
|
}
|
|
|
|
_pendingNetwork = null;
|
|
_pendingSSID = "";
|
|
_pendingWithPsk = false;
|
|
}
|
|
|
|
function connectToWifi(ssid, password = "", username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
|
if (isConnecting) {
|
|
return;
|
|
}
|
|
|
|
const network = _findNetworkBySSID(ssid);
|
|
if (!network) {
|
|
log.warn("SSID not found in scan results:", ssid);
|
|
ToastService.showError(I18n.tr("Network not found"), ssid);
|
|
return;
|
|
}
|
|
|
|
isConnecting = true;
|
|
connectingSSID = ssid;
|
|
connectionError = "";
|
|
connectionStatus = "connecting";
|
|
passwordDialogShouldReopen = false;
|
|
|
|
_pendingNetwork = network;
|
|
_pendingSSID = ssid;
|
|
|
|
if (password) {
|
|
const sec = network.security;
|
|
switch (sec) {
|
|
case WifiSecurityType.WpaPsk:
|
|
case WifiSecurityType.Wpa2Psk:
|
|
case WifiSecurityType.Sae:
|
|
_pendingWithPsk = true;
|
|
network.connectWithPsk(password);
|
|
return;
|
|
default:
|
|
log.warn("Security type not supported with PSK, falling back to connect():", WifiSecurityType.toString(sec));
|
|
}
|
|
}
|
|
|
|
_pendingWithPsk = false;
|
|
network.connect();
|
|
}
|
|
|
|
function disconnectWifi() {
|
|
if (!wifiDevice) {
|
|
return;
|
|
}
|
|
wifiDevice.disconnect();
|
|
connectionStatus = "";
|
|
ToastService.showInfo(I18n.tr("Disconnected from WiFi"));
|
|
}
|
|
|
|
function forgetWifiNetwork(ssid) {
|
|
forgetSSID = ssid;
|
|
const network = _findNetworkBySSID(ssid);
|
|
if (network) {
|
|
network.forget();
|
|
ToastService.showInfo(I18n.tr("Forgot network %1").arg(ssid));
|
|
} else {
|
|
log.warn("Cannot forget, SSID not found:", ssid);
|
|
}
|
|
forgetSSID = "";
|
|
}
|
|
|
|
function toggleWifiRadio() {
|
|
if (wifiToggling) {
|
|
return;
|
|
}
|
|
wifiToggling = true;
|
|
Networking.wifiEnabled = !Networking.wifiEnabled;
|
|
}
|
|
|
|
function enableWifiDevice() {
|
|
if (!Networking.wifiEnabled) {
|
|
Networking.wifiEnabled = true;
|
|
ToastService.showInfo(I18n.tr("WiFi enabled"));
|
|
}
|
|
}
|
|
|
|
function setNetworkPreference(preference) {
|
|
userPreference = preference;
|
|
targetPreference = preference;
|
|
changingPreference = true;
|
|
SettingsData.set("networkPreference", preference);
|
|
setConnectionPriority(preference);
|
|
changingPreference = false;
|
|
targetPreference = "";
|
|
}
|
|
|
|
function setConnectionPriority(type) {
|
|
const wifiMetric = type === "wifi" ? 50 : 100;
|
|
const wiredMetric = type === "ethernet" ? 50 : 100;
|
|
|
|
for (const device of allDevices) {
|
|
let metric = -1;
|
|
switch (device.type) {
|
|
case DeviceType.Wifi:
|
|
metric = wifiMetric;
|
|
break;
|
|
case DeviceType.Wired:
|
|
metric = wiredMetric;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
for (const net of device.networks?.values ?? []) {
|
|
for (const settings of net.nmSettings) {
|
|
settings.write({
|
|
"ipv4": {
|
|
"route-metric": metric
|
|
},
|
|
"ipv6": {
|
|
"route-metric": metric
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function connectToWifiAndSetPreference(ssid, password, username = "", anonymousIdentity = "", domainSuffixMatch = "") {
|
|
connectToWifi(ssid, password, username, anonymousIdentity, domainSuffixMatch);
|
|
setNetworkPreference("wifi");
|
|
}
|
|
|
|
function toggleNetworkConnection(type) {
|
|
if (type !== "ethernet" || !wiredDevice) {
|
|
return;
|
|
}
|
|
if (ethernetConnected) {
|
|
wiredDevice.disconnect();
|
|
} else if (wiredDevice.network) {
|
|
wiredDevice.network.connect();
|
|
}
|
|
}
|
|
|
|
function disconnectEthernetDevice(deviceName) {
|
|
for (const dev of allDevices) {
|
|
if (dev.type === DeviceType.Wired && dev.name === deviceName) {
|
|
dev.disconnect();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function setWifiDeviceOverride(deviceName) {
|
|
SessionData.setWifiDeviceOverride(deviceName || "");
|
|
if (wifiEnabled) {
|
|
scanWifi();
|
|
}
|
|
}
|
|
|
|
function setWifiAutoconnect(ssid, autoconnect) {
|
|
const network = _findNetworkBySSID(ssid);
|
|
if (!network) {
|
|
return;
|
|
}
|
|
for (const settings of network.nmSettings) {
|
|
settings.write({
|
|
"connection": {
|
|
"autoconnect": autoconnect
|
|
}
|
|
});
|
|
}
|
|
ToastService.showInfo(autoconnect ? I18n.tr("Autoconnect enabled") : I18n.tr("Autoconnect disabled"));
|
|
}
|
|
|
|
function fetchNetworkInfo(ssid) {
|
|
networkInfoSSID = ssid;
|
|
networkInfoLoading = false;
|
|
|
|
const network = _findNetworkBySSID(ssid);
|
|
if (!network) {
|
|
networkInfoDetails = "Network information not found or network not available.";
|
|
return;
|
|
}
|
|
|
|
const signalPct = Math.round(network.signalStrength * 100);
|
|
const secLabel = WifiSecurityType.toString(network.security);
|
|
const statusPrefix = network.connected ? "● " : " ";
|
|
const statusSuffix = network.connected ? " (Connected)" : "";
|
|
|
|
let details = statusPrefix + signalPct + "%" + statusSuffix + "\\n";
|
|
details += " Security: " + secLabel + "\\n";
|
|
if (network.known) {
|
|
details += " Status: Saved network\\n";
|
|
}
|
|
networkInfoDetails = details;
|
|
}
|
|
|
|
function fetchWiredNetworkInfo(uuid) {
|
|
networkWiredInfoUUID = uuid;
|
|
networkWiredInfoLoading = false;
|
|
|
|
const dev = wiredDevice;
|
|
if (!dev) {
|
|
networkWiredInfoDetails = "Network information not found or network not available.";
|
|
return;
|
|
}
|
|
|
|
let details = "";
|
|
details += "Interface: " + (dev.name || "-") + "\\n";
|
|
details += "MAC Addr: " + (dev.address || "-") + "\\n";
|
|
details += "Speed: " + (dev.linkSpeed || 0) + " Mb/s\\n\\n";
|
|
|
|
details += "IPv4 information:\\n";
|
|
details += " IPv4 address: " + (ethernetIP || "-") + "\\n";
|
|
|
|
networkWiredInfoDetails = details;
|
|
}
|
|
|
|
function getNetworkInfo(ssid) {
|
|
const network = wifiNetworks.find(n => n.ssid === ssid);
|
|
if (!network) {
|
|
return null;
|
|
}
|
|
return {
|
|
"ssid": network.ssid,
|
|
"signal": network.signal,
|
|
"secured": network.secured,
|
|
"saved": network.saved,
|
|
"connected": network.connected,
|
|
"bssid": network.bssid
|
|
};
|
|
}
|
|
|
|
function getWiredNetworkInfo(uuid) {
|
|
return {
|
|
"uuid": uuid
|
|
};
|
|
}
|
|
|
|
function refreshNetworkState() {
|
|
refreshIPs();
|
|
}
|
|
|
|
function connectToSpecificWiredConfig(uuid) {
|
|
}
|
|
|
|
function submitCredentials(token, secrets, save) {
|
|
}
|
|
|
|
function cancelCredentials(token) {
|
|
}
|
|
|
|
function refreshIPs() {
|
|
getEthernetIP.running = false;
|
|
getWifiIP.running = false;
|
|
|
|
if (ethernetInterface && ethernetConnected) {
|
|
getEthernetIP.command = lowPriorityCmd.concat(["ip", "-4", "addr", "show", ethernetInterface]);
|
|
getEthernetIP.running = true;
|
|
} else {
|
|
ethernetIP = "";
|
|
}
|
|
|
|
if (wifiInterface && wifiConnected) {
|
|
getWifiIP.command = lowPriorityCmd.concat(["ip", "-4", "addr", "show", wifiInterface]);
|
|
getWifiIP.running = true;
|
|
} else {
|
|
wifiIP = "";
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: getEthernetIP
|
|
running: false
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
const match = text.match(/inet (\d+\.\d+\.\d+\.\d+)/);
|
|
root.ethernetIP = match ? match[1] : "";
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: getWifiIP
|
|
running: false
|
|
stdout: StdioCollector {
|
|
onStreamFinished: {
|
|
const match = text.match(/inet (\d+\.\d+\.\d+\.\d+)/);
|
|
root.wifiIP = match ? match[1] : "";
|
|
}
|
|
}
|
|
}
|
|
}
|