diff --git a/CMakeLists.txt b/CMakeLists.txt index 966e8c3..4ed8374 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,7 @@ cmake_minimum_required(VERSION 3.20) project(quickshell VERSION "0.2.1" LANGUAGES CXX C) -set(UNRELEASED_FEATURES - "network.2" - "colorquant-imagerect" -) +set(UNRELEASED_FEATURES) set(QT_MIN_VERSION "6.6.0") set(CMAKE_CXX_STANDARD 20) diff --git a/changelog/next.md b/changelog/next.md index 8b22d07..fc6d79e 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -20,7 +20,7 @@ set shell id. - Added the ability to handle move and resize events to FloatingWindow. - Pipewire service now reconnects if pipewire dies or a protocol error occurs. - Added pipewire audio peak detection. -- Added network management support. +- Added initial support for network management. - Added support for grabbing focus from popup windows. - Added support for IPC signal listeners. - Added Quickshell version checking and version gated preprocessing. @@ -29,7 +29,6 @@ set shell id. - Added generic WindowManager interface implementing ext-workspace. - Added ext-background-effect window blur support. - Added per-corner radius support to Region. -- Added ColorQuantizer region selection. ## Other Changes @@ -65,7 +64,6 @@ set shell id. - Fixed partial socket reads in greetd and hyprland on slow machines. - Worked around Qt bug causing crashes when plugging and unplugging monitors. - Fixed HyprlandFocusGrab crashing if windows were destroyed after being passed to it. -- Fixed ScreencopyView pixelation when scaled. ## Packaging Changes diff --git a/src/core/colorquantizer.cpp b/src/core/colorquantizer.cpp index d983f76..4ac850b 100644 --- a/src/core/colorquantizer.cpp +++ b/src/core/colorquantizer.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -25,15 +24,9 @@ namespace { QS_LOGGING_CATEGORY(logColorQuantizer, "quickshell.colorquantizer", QtWarningMsg); } -ColorQuantizerOperation::ColorQuantizerOperation( - QUrl* source, - qreal depth, - QRect imageRect, - qreal rescaleSize -) +ColorQuantizerOperation::ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize) : source(source) , maxDepth(depth) - , imageRect(imageRect) , rescaleSize(rescaleSize) { this->setAutoDelete(false); } @@ -44,11 +37,6 @@ void ColorQuantizerOperation::quantizeImage(const QAtomicInteger& shouldCa this->colors.clear(); auto image = QImage(this->source->toLocalFile()); - - if (this->imageRect.isValid()) { - image = image.copy(this->imageRect); - } - if ((image.width() > this->rescaleSize || image.height() > this->rescaleSize) && this->rescaleSize > 0) { @@ -210,27 +198,16 @@ void ColorQuantizer::setDepth(qreal depth) { this->mDepth = depth; emit this->depthChanged(); - if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync(); + if (this->componentCompleted) this->quantizeAsync(); } } -void ColorQuantizer::setImageRect(QRect imageRect) { - if (this->mImageRect != imageRect) { - this->mImageRect = imageRect; - emit this->imageRectChanged(); - - if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync(); - } -} - -void ColorQuantizer::resetImageRect() { this->setImageRect(QRect()); } - void ColorQuantizer::setRescaleSize(int rescaleSize) { if (this->mRescaleSize != rescaleSize) { this->mRescaleSize = rescaleSize; emit this->rescaleSizeChanged(); - if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync(); + if (this->componentCompleted) this->quantizeAsync(); } } @@ -244,13 +221,8 @@ void ColorQuantizer::quantizeAsync() { if (this->liveOperation) this->cancelAsync(); qCDebug(logColorQuantizer) << "Starting color quantization asynchronously"; - - this->liveOperation = new ColorQuantizerOperation( - &this->mSource, - this->mDepth, - this->mImageRect, - this->mRescaleSize - ); + this->liveOperation = + new ColorQuantizerOperation(&this->mSource, this->mDepth, this->mRescaleSize); QObject::connect( this->liveOperation, diff --git a/src/core/colorquantizer.hpp b/src/core/colorquantizer.hpp index 0159181..f6e158d 100644 --- a/src/core/colorquantizer.hpp +++ b/src/core/colorquantizer.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -17,7 +16,7 @@ class ColorQuantizerOperation Q_OBJECT; public: - explicit ColorQuantizerOperation(QUrl* source, qreal depth, QRect imageRect, qreal rescaleSize); + explicit ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize); void run() override; void tryCancel(); @@ -45,7 +44,6 @@ private: QList colors; QUrl* source; qreal maxDepth; - QRect imageRect; qreal rescaleSize; }; @@ -80,13 +78,6 @@ class ColorQuantizer /// binary split of the color space Q_PROPERTY(qreal depth READ depth WRITE setDepth NOTIFY depthChanged); - // clang-format off - /// Rectangle that the source image is cropped to. - /// - /// Can be set to `undefined` to reset. - Q_PROPERTY(QRect imageRect READ imageRect WRITE setImageRect RESET resetImageRect NOTIFY imageRectChanged); - // clang-format on - /// The size to rescale the image to, when rescaleSize is 0 then no scaling will be done. /// > [!NOTE] Results from color quantization doesn't suffer much when rescaling, it's /// > reccommended to rescale, otherwise the quantization process will take much longer. @@ -106,10 +97,6 @@ public: [[nodiscard]] qreal depth() const { return this->mDepth; } void setDepth(qreal depth); - [[nodiscard]] QRect imageRect() const { return this->mImageRect; } - void setImageRect(QRect imageRect); - void resetImageRect(); - [[nodiscard]] qreal rescaleSize() const { return this->mRescaleSize; } void setRescaleSize(int rescaleSize); @@ -117,7 +104,6 @@ signals: void colorsChanged(); void sourceChanged(); void depthChanged(); - void imageRectChanged(); void rescaleSizeChanged(); public slots: @@ -131,7 +117,6 @@ private: ColorQuantizerOperation* liveOperation = nullptr; QUrl mSource; qreal mDepth = 0; - QRect mImageRect; qreal mRescaleSize = 0; Q_OBJECT_BINDABLE_PROPERTY( diff --git a/src/core/toolsupport.cpp b/src/core/toolsupport.cpp index 585656e..8aa5ac9 100644 --- a/src/core/toolsupport.cpp +++ b/src/core/toolsupport.cpp @@ -177,8 +177,6 @@ void QmlToolingSupport::updateToolingFs( auto fileInfo = QFileInfo(path); if (!fileInfo.isFile()) continue; - if (scanner.fileIntercepts.contains(path)) continue; - auto spath = linkDir.filePath(name); auto sFileInfo = QFileInfo(spath); @@ -207,10 +205,8 @@ void QmlToolingSupport::updateToolingFs( } auto spath = linkDir.filePath(name); - QFile::remove(spath); - auto file = QFile(spath); - if (!file.open(QFile::ReadWrite | QFile::Text | QFile::NewOnly)) { + if (!file.open(QFile::ReadWrite | QFile::Text)) { qCCritical(logTooling) << "Failed to open injected file" << spath; continue; } diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index 03ef86a..6075040 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -4,7 +4,6 @@ qt_add_library(quickshell-network STATIC network.cpp device.cpp wifi.cpp - enums.cpp ) target_include_directories(quickshell-network PRIVATE diff --git a/src/network/device.cpp b/src/network/device.cpp index 5679e8d..22e3949 100644 --- a/src/network/device.cpp +++ b/src/network/device.cpp @@ -8,7 +8,6 @@ #include #include "../core/logcat.hpp" -#include "enums.hpp" namespace qs::network { @@ -16,9 +15,49 @@ namespace { QS_LOGGING_CATEGORY(logNetworkDevice, "quickshell.network.device", QtWarningMsg); } // namespace +QString DeviceConnectionState::toString(DeviceConnectionState::Enum state) { + switch (state) { + case Unknown: return QStringLiteral("Unknown"); + case Connecting: return QStringLiteral("Connecting"); + case Connected: return QStringLiteral("Connected"); + case Disconnecting: return QStringLiteral("Disconnecting"); + case Disconnected: return QStringLiteral("Disconnected"); + default: return QStringLiteral("Unknown"); + } +} + +QString DeviceType::toString(DeviceType::Enum type) { + switch (type) { + case None: return QStringLiteral("None"); + case Wifi: return QStringLiteral("Wifi"); + default: return QStringLiteral("Unknown"); + } +} + +QString NMDeviceState::toString(NMDeviceState::Enum state) { + switch (state) { + case Unknown: return QStringLiteral("Unknown"); + case Unmanaged: return QStringLiteral("Not managed by NetworkManager"); + case Unavailable: return QStringLiteral("Unavailable"); + case Disconnected: return QStringLiteral("Disconnected"); + case Prepare: return QStringLiteral("Preparing to connect"); + case Config: return QStringLiteral("Connecting to a network"); + case NeedAuth: return QStringLiteral("Waiting for authentication"); + case IPConfig: return QStringLiteral("Requesting IPv4 and/or IPv6 addresses from the network"); + case IPCheck: + return QStringLiteral("Checking if further action is required for the requested connection"); + case Secondaries: + return QStringLiteral("Waiting for a required secondary connection to activate"); + case Activated: return QStringLiteral("Connected"); + case Deactivating: return QStringLiteral("Disconnecting"); + case Failed: return QStringLiteral("Failed to connect"); + default: return QStringLiteral("Unknown"); + }; +} + NetworkDevice::NetworkDevice(DeviceType::Enum type, QObject* parent): QObject(parent), mType(type) { this->bindableConnected().setBinding([this]() { - return this->bState == ConnectionState::Connected; + return this->bState == DeviceConnectionState::Connected; }); }; @@ -27,17 +66,12 @@ void NetworkDevice::setAutoconnect(bool autoconnect) { emit this->requestSetAutoconnect(autoconnect); } -void NetworkDevice::setNmManaged(bool managed) { - if (this->bNmManaged == managed) return; - emit this->requestSetNmManaged(managed); -} - void NetworkDevice::disconnect() { - if (this->bState == ConnectionState::Disconnected) { + if (this->bState == DeviceConnectionState::Disconnected) { qCCritical(logNetworkDevice) << "Device" << this << "is already disconnected"; return; } - if (this->bState == ConnectionState::Disconnecting) { + if (this->bState == DeviceConnectionState::Disconnecting) { qCCritical(logNetworkDevice) << "Device" << this << "is already disconnecting"; return; } diff --git a/src/network/device.hpp b/src/network/device.hpp index 8d914a1..f3807c2 100644 --- a/src/network/device.hpp +++ b/src/network/device.hpp @@ -6,22 +6,76 @@ #include #include -#include "../core/doc.hpp" -#include "enums.hpp" - namespace qs::network { +///! Connection state of a NetworkDevice. +class DeviceConnectionState: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + Unknown = 0, + Connecting = 1, + Connected = 2, + Disconnecting = 3, + Disconnected = 4, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(DeviceConnectionState::Enum state); +}; + +///! Type of network device. +class DeviceType: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + None = 0, + Wifi = 1, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(DeviceType::Enum type); +}; + +///! NetworkManager-specific device state. +/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceState. +class NMDeviceState: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + Unknown = 0, + Unmanaged = 10, + Unavailable = 20, + Disconnected = 30, + Prepare = 40, + Config = 50, + NeedAuth = 60, + IPConfig = 70, + IPCheck = 80, + Secondaries = 90, + Activated = 100, + Deactivating = 110, + Failed = 120, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(NMDeviceState::Enum state); +}; + ///! A network device. -/// The @@type property may be used to determine if this device is a @@WifiDevice. +/// When @@type is `Wifi`, the device is a @@WifiDevice, which can be used to scan for and connect to access points. class NetworkDevice: public QObject { Q_OBJECT; QML_ELEMENT; QML_UNCREATABLE("Devices can only be acquired through Network"); // clang-format off /// The device type. - /// - /// When the device type is `Wifi`, the device object is a @@WifiDevice which exposes wifi network - /// connection and scanning. Q_PROPERTY(DeviceType::Enum type READ type CONSTANT); /// The name of the device's control interface. Q_PROPERTY(QString name READ name NOTIFY nameChanged BINDABLE bindableName); @@ -30,12 +84,10 @@ class NetworkDevice: public QObject { /// True if the device is connected. Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected); /// Connection state of the device. - Q_PROPERTY(qs::network::ConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); - /// True if the device is managed by NetworkManager. - /// - /// > [!WARNING] Only valid for the NetworkManager backend. - Q_PROPERTY(bool nmManaged READ nmManaged WRITE setNmManaged NOTIFY nmManagedChanged) - /// True if the device is allowed to autoconnect to a network. + Q_PROPERTY(qs::network::DeviceConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); + /// A more specific device state when the backend is NetworkManager. + Q_PROPERTY(qs::network::NMDeviceState::Enum nmState READ default NOTIFY nmStateChanged BINDABLE bindableNmState); + /// True if the device is allowed to autoconnect. Q_PROPERTY(bool autoconnect READ autoconnect WRITE setAutoconnect NOTIFY autoconnectChanged); // clang-format on @@ -45,28 +97,25 @@ public: /// Disconnects the device and prevents it from automatically activating further connections. Q_INVOKABLE void disconnect(); - [[nodiscard]] DeviceType::Enum type() const { return this->mType; } - QBindable bindableName() { return &this->bName; } - [[nodiscard]] QString name() const { return this->bName; } - QBindable bindableAddress() { return &this->bAddress; } - QBindable bindableConnected() { return &this->bConnected; } - QBindable bindableState() { return &this->bState; } - QBindable bindableNmManaged() { return &this->bNmManaged; } - [[nodiscard]] bool nmManaged() { return this->bNmManaged; } - void setNmManaged(bool managed); - QBindable bindableAutoconnect() { return &this->bAutoconnect; } - [[nodiscard]] bool autoconnect() { return this->bAutoconnect; } + [[nodiscard]] DeviceType::Enum type() const { return this->mType; }; + QBindable bindableName() { return &this->bName; }; + [[nodiscard]] QString name() const { return this->bName; }; + QBindable bindableAddress() { return &this->bAddress; }; + QBindable bindableConnected() { return &this->bConnected; }; + QBindable bindableState() { return &this->bState; }; + QBindable bindableNmState() { return &this->bNmState; }; + [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; }; + QBindable bindableAutoconnect() { return &this->bAutoconnect; }; void setAutoconnect(bool autoconnect); signals: - QSDOC_HIDE void requestDisconnect(); - QSDOC_HIDE void requestSetAutoconnect(bool autoconnect); - QSDOC_HIDE void requestSetNmManaged(bool managed); + void requestDisconnect(); + void requestSetAutoconnect(bool autoconnect); void nameChanged(); void addressChanged(); void connectedChanged(); void stateChanged(); - void nmManagedChanged(); + void nmStateChanged(); void autoconnectChanged(); private: @@ -75,8 +124,8 @@ private: Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bName, &NetworkDevice::nameChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bAddress, &NetworkDevice::addressChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bConnected, &NetworkDevice::connectedChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, ConnectionState::Enum, bState, &NetworkDevice::stateChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bNmManaged, &NetworkDevice::nmManagedChanged); + Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, DeviceConnectionState::Enum, bState, &NetworkDevice::stateChanged); + Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, NMDeviceState::Enum, bNmState, &NetworkDevice::nmStateChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bAutoconnect, &NetworkDevice::autoconnectChanged); // clang-format on }; diff --git a/src/network/enums.cpp b/src/network/enums.cpp deleted file mode 100644 index 2cf36c1..0000000 --- a/src/network/enums.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "enums.hpp" - -#include - -namespace qs::network { - -QString NetworkConnectivity::toString(NetworkConnectivity::Enum conn) { - switch (conn) { - case Unknown: return QStringLiteral("Unknown"); - case None: return QStringLiteral("Not connected to a network"); - case Portal: return QStringLiteral("Connection intercepted by a captive portal"); - case Limited: return QStringLiteral("Partial internet connectivity"); - case Full: return QStringLiteral("Full internet connectivity"); - default: return QStringLiteral("Unknown"); - } -} - -QString NetworkBackendType::toString(NetworkBackendType::Enum type) { - switch (type) { - case NetworkBackendType::None: return "None"; - case NetworkBackendType::NetworkManager: return "NetworkManager"; - default: return "Unknown"; - } -} - -QString ConnectionState::toString(ConnectionState::Enum state) { - switch (state) { - case Unknown: return QStringLiteral("Unknown"); - case Connecting: return QStringLiteral("Connecting"); - case Connected: return QStringLiteral("Connected"); - case Disconnecting: return QStringLiteral("Disconnecting"); - case Disconnected: return QStringLiteral("Disconnected"); - default: return QStringLiteral("Unknown"); - } -} - -QString ConnectionFailReason::toString(ConnectionFailReason::Enum reason) { - switch (reason) { - case Unknown: return QStringLiteral("Unknown"); - case NoSecrets: return QStringLiteral("Secrets were required but not provided"); - case WifiClientDisconnected: return QStringLiteral("Wi-Fi supplicant diconnected"); - case WifiClientFailed: return QStringLiteral("Wi-Fi supplicant failed"); - case WifiAuthTimeout: return QStringLiteral("Wi-Fi connection took too long to authenticate"); - case WifiNetworkLost: return QStringLiteral("Wi-Fi network could not be found"); - default: return QStringLiteral("Unknown"); - } -} - -QString DeviceType::toString(DeviceType::Enum type) { - switch (type) { - case None: return QStringLiteral("None"); - case Wifi: return QStringLiteral("Wifi"); - default: return QStringLiteral("Unknown"); - } -} - -QString WifiSecurityType::toString(WifiSecurityType::Enum type) { - switch (type) { - case Unknown: return QStringLiteral("Unknown"); - case Wpa3SuiteB192: return QStringLiteral("WPA3 Suite B 192-bit"); - case Sae: return QStringLiteral("WPA3"); - case Wpa2Eap: return QStringLiteral("WPA2 Enterprise"); - case Wpa2Psk: return QStringLiteral("WPA2"); - case WpaEap: return QStringLiteral("WPA Enterprise"); - case WpaPsk: return QStringLiteral("WPA"); - case StaticWep: return QStringLiteral("WEP"); - case DynamicWep: return QStringLiteral("Dynamic WEP"); - case Leap: return QStringLiteral("LEAP"); - case Owe: return QStringLiteral("OWE"); - case Open: return QStringLiteral("Open"); - default: return QStringLiteral("Unknown"); - } -} - -QString WifiDeviceMode::toString(WifiDeviceMode::Enum mode) { - switch (mode) { - case Unknown: return QStringLiteral("Unknown"); - case AdHoc: return QStringLiteral("Ad-Hoc"); - case Station: return QStringLiteral("Station"); - case AccessPoint: return QStringLiteral("Access Point"); - case Mesh: return QStringLiteral("Mesh"); - default: return QStringLiteral("Unknown"); - }; -} - -} // namespace qs::network diff --git a/src/network/enums.hpp b/src/network/enums.hpp deleted file mode 100644 index 49c28ce..0000000 --- a/src/network/enums.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace qs::network { - -///! The degree to which the host can reach the internet. -class NetworkConnectivity: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - /// Network connectivity is unknown. This means the connectivity checks are disabled or have not run yet. - Unknown = 0, - /// The host is not connected to any network. - None = 1, - /// The internet connection is hijacked by a captive portal gateway. - /// This indicates the shell should open a sandboxed web browser window for the purpose of authenticating to a gateway. - Portal = 2, - /// The host is connected to a network but does not appear to be able to reach the full internet. - Limited = 3, - /// The host is connected to a network and appears to be able to reach the full internet. - Full = 4, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(NetworkConnectivity::Enum conn); -}; - -///! The backend supplying the Network service. -class NetworkBackendType: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - None = 0, - NetworkManager = 1, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(NetworkBackendType::Enum type); -}; - -///! The connection state of a device or network. -class ConnectionState: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - Unknown = 0, - Connecting = 1, - Connected = 2, - Disconnecting = 3, - Disconnected = 4, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(ConnectionState::Enum state); -}; - -///! The reason a connection failed. -class ConnectionFailReason: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - /// The connection failed for an unknown reason. - Unknown = 0, - /// Secrets were required, but not provided. - NoSecrets = 1, - /// The Wi-Fi supplicant disconnected. - WifiClientDisconnected = 2, - /// The Wi-Fi supplicant failed. - WifiClientFailed = 3, - /// The Wi-Fi connection took too long to authenticate. - WifiAuthTimeout = 4, - /// The Wi-Fi network could not be found. - WifiNetworkLost = 5, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(ConnectionFailReason::Enum reason); -}; - -///! Type of a @@NetworkDevice. -class DeviceType: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - None = 0, - Wifi = 1, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(DeviceType::Enum type); -}; - -///! The security type of a @@WifiNetwork. -class WifiSecurityType: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - Wpa3SuiteB192 = 0, - Sae = 1, - Wpa2Eap = 2, - Wpa2Psk = 3, - WpaEap = 4, - WpaPsk = 5, - StaticWep = 6, - DynamicWep = 7, - Leap = 8, - Owe = 9, - Open = 10, - Unknown = 11, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(WifiSecurityType::Enum type); -}; - -///! The 802.11 mode of a @@WifiDevice. -class WifiDeviceMode: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - /// The device is part of an Ad-Hoc network without a central access point. - AdHoc = 0, - /// The device is a station that can connect to networks. - Station = 1, - /// The device is a local hotspot/access point. - AccessPoint = 2, - /// The device is an 802.11s mesh point. - Mesh = 3, - /// The device mode is unknown. - Unknown = 4, - }; - Q_ENUM(Enum); - Q_INVOKABLE static QString toString(WifiDeviceMode::Enum mode); -}; - -} // namespace qs::network diff --git a/src/network/module.md b/src/network/module.md index 91ff2f1..a0c8e64 100644 --- a/src/network/module.md +++ b/src/network/module.md @@ -4,8 +4,6 @@ headers = [ "network.hpp", "device.hpp", "wifi.hpp", - "enums.hpp", - "nm/settings.hpp", ] ----- This module exposes Network management APIs provided by a supported network backend. diff --git a/src/network/network.cpp b/src/network/network.cpp index e66ffa6..e325b05 100644 --- a/src/network/network.cpp +++ b/src/network/network.cpp @@ -1,7 +1,6 @@ #include "network.hpp" #include -#include #include #include #include @@ -10,9 +9,7 @@ #include "../core/logcat.hpp" #include "device.hpp" -#include "enums.hpp" #include "nm/backend.hpp" -#include "nm/settings.hpp" namespace qs::network { @@ -20,22 +17,25 @@ namespace { QS_LOGGING_CATEGORY(logNetwork, "quickshell.network", QtWarningMsg); } // namespace +QString NetworkState::toString(NetworkState::Enum state) { + switch (state) { + case NetworkState::Connecting: return QStringLiteral("Connecting"); + case NetworkState::Connected: return QStringLiteral("Connected"); + case NetworkState::Disconnecting: return QStringLiteral("Disconnecting"); + case NetworkState::Disconnected: return QStringLiteral("Disconnected"); + default: return QStringLiteral("Unknown"); + } +} + Networking::Networking(QObject* parent): QObject(parent) { // Try to create the NetworkManager backend and bind to it. auto* nm = new NetworkManager(this); if (nm->isAvailable()) { - // clang-format off QObject::connect(nm, &NetworkManager::deviceAdded, this, &Networking::deviceAdded); QObject::connect(nm, &NetworkManager::deviceRemoved, this, &Networking::deviceRemoved); QObject::connect(this, &Networking::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled); - QObject::connect(this, &Networking::requestSetConnectivityCheckEnabled, nm, &NetworkManager::setConnectivityCheckEnabled); - QObject::connect(this, &Networking::requestCheckConnectivity, nm, &NetworkManager::checkConnectivity); this->bindableWifiEnabled().setBinding([nm]() { return nm->wifiEnabled(); }); this->bindableWifiHardwareEnabled().setBinding([nm]() { return nm->wifiHardwareEnabled(); }); - this->bindableCanCheckConnectivity().setBinding([nm]() { return nm->connectivityCheckAvailable(); }); - this->bindableConnectivityCheckEnabled().setBinding([nm]() { return nm->connectivityCheckEnabled(); }); - this->bindableConnectivity().setBinding([nm]() { return static_cast(nm->connectivity()); }); - // clang-format on this->mBackend = nm; this->mBackendType = NetworkBackendType::NetworkManager; @@ -43,89 +43,23 @@ Networking::Networking(QObject* parent): QObject(parent) { } else { delete nm; } - qCCritical(logNetwork) << "Network will not work. Could not find an available backend."; -} -Networking* Networking::instance() { - static Networking* instance = new Networking(); // NOLINT - return instance; + qCCritical(logNetwork) << "Network will not work. Could not find an available backend."; } void Networking::deviceAdded(NetworkDevice* dev) { this->mDevices.insertObject(dev); } void Networking::deviceRemoved(NetworkDevice* dev) { this->mDevices.removeObject(dev); } -void Networking::checkConnectivity() { - if (!this->bConnectivityCheckEnabled || !this->bCanCheckConnectivity) return; - emit this->requestCheckConnectivity(); -} - void Networking::setWifiEnabled(bool enabled) { if (this->bWifiEnabled == enabled) return; emit this->requestSetWifiEnabled(enabled); } -void Networking::setConnectivityCheckEnabled(bool enabled) { - if (this->bConnectivityCheckEnabled == enabled) return; - emit this->requestSetConnectivityCheckEnabled(enabled); -} - -NetworkingQml::NetworkingQml(QObject* parent): QObject(parent) { - // clang-format off - QObject::connect(Networking::instance(), &Networking::wifiEnabledChanged, this, &NetworkingQml::wifiEnabledChanged); - QObject::connect(Networking::instance(), &Networking::wifiHardwareEnabledChanged, this, &NetworkingQml::wifiHardwareEnabledChanged); - QObject::connect(Networking::instance(), &Networking::canCheckConnectivityChanged, this, &NetworkingQml::canCheckConnectivityChanged); - QObject::connect(Networking::instance(), &Networking::connectivityCheckEnabledChanged, this, &NetworkingQml::connectivityCheckEnabledChanged); - QObject::connect(Networking::instance(), &Networking::connectivityChanged, this, &NetworkingQml::connectivityChanged); - // clang-format on -} - -void NetworkingQml::checkConnectivity() { Networking::instance()->checkConnectivity(); } - Network::Network(QString name, QObject* parent): QObject(parent), mName(std::move(name)) { this->bStateChanging.setBinding([this] { auto state = this->bState.value(); - return state == ConnectionState::Connecting || state == ConnectionState::Disconnecting; + return state == NetworkState::Connecting || state == NetworkState::Disconnecting; }); }; -void Network::connect() { - if (this->bConnected) { - qCCritical(logNetwork) << this << "is already connected."; - return; - } - this->requestConnect(); -} - -void Network::connectWithSettings(NMSettings* settings) { - if (this->bConnected) { - qCCritical(logNetwork) << this << "is already connected."; - return; - } - if (this->bNmSettings.value().indexOf(settings) == -1) return; - this->requestConnectWithSettings(settings); -} - -void Network::disconnect() { - if (!this->bConnected) { - qCCritical(logNetwork) << this << "is not currently connected"; - return; - } - this->requestDisconnect(); -} - -void Network::forget() { this->requestForget(); } - -void Network::settingsAdded(NMSettings* settings) { - auto list = this->bNmSettings.value(); - if (list.contains(settings)) return; - list.append(settings); - this->bNmSettings = list; -} - -void Network::settingsRemoved(NMSettings* settings) { - auto list = this->bNmSettings.value(); - list.removeOne(settings); - this->bNmSettings = list; -} - } // namespace qs::network diff --git a/src/network/network.hpp b/src/network/network.hpp index f7734a2..8af7c9d 100644 --- a/src/network/network.hpp +++ b/src/network/network.hpp @@ -6,14 +6,43 @@ #include #include -#include "../core/doc.hpp" #include "../core/model.hpp" #include "device.hpp" -#include "enums.hpp" -#include "nm/settings.hpp" namespace qs::network { +///! The connection state of a Network. +class NetworkState: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + Unknown = 0, + Connecting = 1, + Connected = 2, + Disconnecting = 3, + Disconnected = 4, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(NetworkState::Enum state); +}; + +///! The backend supplying the Network service. +class NetworkBackendType: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + None = 0, + NetworkManager = 1, + }; + Q_ENUM(Enum); +}; + class NetworkBackend: public QObject { Q_OBJECT; @@ -24,65 +53,15 @@ protected: explicit NetworkBackend(QObject* parent = nullptr): QObject(parent) {}; }; -class Networking: public QObject { - Q_OBJECT; - -public: - static Networking* instance(); - - void checkConnectivity(); - - [[nodiscard]] ObjectModel* devices() { return &this->mDevices; } - [[nodiscard]] NetworkBackendType::Enum backend() const { return this->mBackendType; } - QBindable bindableWifiEnabled() { return &this->bWifiEnabled; } - [[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; } - void setWifiEnabled(bool enabled); - QBindable bindableWifiHardwareEnabled() { return &this->bWifiHardwareEnabled; } - QBindable bindableCanCheckConnectivity() { return &this->bCanCheckConnectivity; } - QBindable bindableConnectivityCheckEnabled() { return &this->bConnectivityCheckEnabled; } - [[nodiscard]] bool connectivityCheckEnabled() const { return this->bConnectivityCheckEnabled; } - void setConnectivityCheckEnabled(bool enabled); - QBindable bindableConnectivity() { return &this->bConnectivity; } - -signals: - void requestSetWifiEnabled(bool enabled); - void requestSetConnectivityCheckEnabled(bool enabled); - void requestCheckConnectivity(); - - void wifiEnabledChanged(); - void wifiHardwareEnabledChanged(); - void canCheckConnectivityChanged(); - void connectivityCheckEnabledChanged(); - void connectivityChanged(); - -private slots: - void deviceAdded(NetworkDevice* dev); - void deviceRemoved(NetworkDevice* dev); - -private: - explicit Networking(QObject* parent = nullptr); - - ObjectModel mDevices {this}; - NetworkBackend* mBackend = nullptr; - NetworkBackendType::Enum mBackendType = NetworkBackendType::None; - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiEnabled, &Networking::wifiEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiHardwareEnabled, &Networking::wifiHardwareEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bCanCheckConnectivity, &Networking::canCheckConnectivityChanged); - Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bConnectivityCheckEnabled, &Networking::connectivityCheckEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(Networking, NetworkConnectivity::Enum, bConnectivity, &Networking::connectivityChanged); - // clang-format on -}; - ///! The Network service. /// An interface to a network backend (currently only NetworkManager), /// which can be used to view, configure, and connect to various networks. -class NetworkingQml: public QObject { +class Networking: public QObject { Q_OBJECT; - QML_NAMED_ELEMENT(Networking); QML_SINGLETON; + QML_ELEMENT; // clang-format off - /// A list of all network devices. Networks are exposed through their respective devices. + /// A list of all network devices. QSDOC_TYPE_OVERRIDE(ObjectModel*); Q_PROPERTY(UntypedObjectModel* devices READ devices CONSTANT); /// The backend being used to power the Network service. @@ -91,143 +70,73 @@ class NetworkingQml: public QObject { Q_PROPERTY(bool wifiEnabled READ wifiEnabled WRITE setWifiEnabled NOTIFY wifiEnabledChanged); /// State of the rfkill hardware block of all wireless devices. Q_PROPERTY(bool wifiHardwareEnabled READ default NOTIFY wifiHardwareEnabledChanged BINDABLE bindableWifiHardwareEnabled); - /// True if the @@backend supports connectivity checks. - Q_PROPERTY(bool canCheckConnectivity READ default NOTIFY canCheckConnectivityChanged BINDABLE bindableCanCheckConnectivity); - /// True if connectivity checking is enabled. - Q_PROPERTY(bool connectivityCheckEnabled READ connectivityCheckEnabled WRITE setConnectivityCheckEnabled NOTIFY connectivityCheckEnabledChanged); - /// The result of the last connectivity check. - /// - /// Connectivity checks may require additional configuration depending on your distro. - /// - /// > [!NOTE] This property can be used to determine if network access is restricted - /// > or gated behind a captive portal. - /// > - /// > If checking for captive portals, @@checkConnectivity() should be called after - /// > the portal is dismissed to update this property. - Q_PROPERTY(qs::network::NetworkConnectivity::Enum connectivity READ default NOTIFY connectivityChanged BINDABLE bindableConnectivity); // clang-format on public: - explicit NetworkingQml(QObject* parent = nullptr); + explicit Networking(QObject* parent = nullptr); - /// Re-check the network connectivity state immediately. - /// > [!NOTE] This should be invoked after a user dismisses a web browser that was opened to authenticate via a captive portal. - Q_INVOKABLE static void checkConnectivity(); - - [[nodiscard]] static ObjectModel* devices() { - return Networking::instance()->devices(); - } - [[nodiscard]] static NetworkBackendType::Enum backend() { - return Networking::instance()->backend(); - } - [[nodiscard]] static bool wifiEnabled() { return Networking::instance()->wifiEnabled(); } - static void setWifiEnabled(bool enabled) { Networking::instance()->setWifiEnabled(enabled); } - [[nodiscard]] static QBindable bindableWifiHardwareEnabled() { - return Networking::instance()->bindableWifiHardwareEnabled(); - } - [[nodiscard]] static QBindable bindableWifiEnabled() { - return Networking::instance()->bindableWifiEnabled(); - } - [[nodiscard]] static QBindable bindableCanCheckConnectivity() { - return Networking::instance()->bindableCanCheckConnectivity(); - } - [[nodiscard]] static bool connectivityCheckEnabled() { - return Networking::instance()->connectivityCheckEnabled(); - } - static void setConnectivityCheckEnabled(bool enabled) { - Networking::instance()->setConnectivityCheckEnabled(enabled); - } - [[nodiscard]] static QBindable bindableConnectivity() { - return Networking::instance()->bindableConnectivity(); - } + [[nodiscard]] ObjectModel* devices() { return &this->mDevices; }; + [[nodiscard]] NetworkBackendType::Enum backend() const { return this->mBackendType; }; + QBindable bindableWifiEnabled() { return &this->bWifiEnabled; }; + [[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; }; + void setWifiEnabled(bool enabled); + QBindable bindableWifiHardwareEnabled() { return &this->bWifiHardwareEnabled; }; signals: + void requestSetWifiEnabled(bool enabled); void wifiEnabledChanged(); void wifiHardwareEnabledChanged(); - void canCheckConnectivityChanged(); - void connectivityCheckEnabledChanged(); - void connectivityChanged(); + +private slots: + void deviceAdded(NetworkDevice* dev); + void deviceRemoved(NetworkDevice* dev); + +private: + ObjectModel mDevices {this}; + NetworkBackend* mBackend = nullptr; + NetworkBackendType::Enum mBackendType = NetworkBackendType::None; + // clang-format off + Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiEnabled, &Networking::wifiEnabledChanged); + Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiHardwareEnabled, &Networking::wifiHardwareEnabledChanged); + // clang-format on }; ///! A network. -/// A network. Networks derived from a @@WifiDevice are @@WifiNetwork instances. class Network: public QObject { Q_OBJECT; QML_ELEMENT; - QML_UNCREATABLE("Network can only be aqcuired through networking devices"); + QML_UNCREATABLE("BaseNetwork can only be aqcuired through network devices"); // clang-format off /// The name of the network. Q_PROPERTY(QString name READ name CONSTANT); - /// A list of NetworkManager connnection settings profiles for this network. - /// - /// > [!WARNING] Only valid for the NetworkManager backend. - Q_PROPERTY(QList nmSettings READ nmSettings NOTIFY nmSettingsChanged BINDABLE bindableNmSettings); /// True if the network is connected. Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected); - /// True if the wifi network has known connection settings saved. - Q_PROPERTY(bool known READ default NOTIFY knownChanged BINDABLE bindableKnown); /// The connectivity state of the network. - Q_PROPERTY(ConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); + Q_PROPERTY(NetworkState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); /// If the network is currently connecting or disconnecting. Shorthand for checking @@state. Q_PROPERTY(bool stateChanging READ default NOTIFY stateChangingChanged BINDABLE bindableStateChanging); // clang-format on public: explicit Network(QString name, QObject* parent = nullptr); - /// Attempt to connect to the network. - /// - /// > [!NOTE] If the network is a @@WifiNetwork and requires secrets, a @@connectionFailed(s) - /// > signal will be emitted with `NoSecrets`. - /// > @@WifiNetwork.connectWithPsk() can be used to provide secrets. - Q_INVOKABLE void connect(); - /// Attempt to connect to the network with a specific @@nmSettings entry. - /// - /// > [!WARNING] Only valid for the NetworkManager backend. - Q_INVOKABLE void connectWithSettings(NMSettings* settings); - /// Disconnect from the network. - Q_INVOKABLE void disconnect(); - /// Forget all connection settings for this network. - Q_INVOKABLE void forget(); - void settingsAdded(NMSettings* settings); - void settingsRemoved(NMSettings* settings); - - // clang-format off - [[nodiscard]] QString name() const { return this->mName; } - [[nodiscard]] const QList& nmSettings() const { return this->bNmSettings; } - QBindable> bindableNmSettings() const { return &this->bNmSettings; } + [[nodiscard]] QString name() const { return this->mName; }; QBindable bindableConnected() { return &this->bConnected; } - QBindable bindableKnown() { return &this->bKnown; } - [[nodiscard]] ConnectionState::Enum state() const { return this->bState; } - QBindable bindableState() { return &this->bState; } + QBindable bindableState() { return &this->bState; } QBindable bindableStateChanging() { return &this->bStateChanging; } - // clang-format on signals: - /// Signals that a connection to the network has failed because of the given @@ConnectionFailReason. - void connectionFailed(ConnectionFailReason::Enum reason); - void connectedChanged(); - void knownChanged(); void stateChanged(); void stateChangingChanged(); - void nmSettingsChanged(); - QSDOC_HIDE void requestConnect(); - QSDOC_HIDE void requestConnectWithSettings(NMSettings* settings); - QSDOC_HIDE void requestDisconnect(); - QSDOC_HIDE void requestForget(); protected: QString mName; - // clang-format off Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bConnected, &Network::connectedChanged); - Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bKnown, &Network::knownChanged); - Q_OBJECT_BINDABLE_PROPERTY(Network, ConnectionState::Enum, bState, &Network::stateChanged); + Q_OBJECT_BINDABLE_PROPERTY(Network, NetworkState::Enum, bState, &Network::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bStateChanging, &Network::stateChangingChanged); - Q_OBJECT_BINDABLE_PROPERTY(Network, QList, bNmSettings, &Network::nmSettingsChanged); - // clang-format on }; } // namespace qs::network diff --git a/src/network/nm/CMakeLists.txt b/src/network/nm/CMakeLists.txt index 61f7e66..bb8635e 100644 --- a/src/network/nm/CMakeLists.txt +++ b/src/network/nm/CMakeLists.txt @@ -63,12 +63,10 @@ qt_add_dbus_interface(NM_DBUS_INTERFACES qt_add_library(quickshell-network-nm STATIC backend.cpp device.cpp - active_connection.cpp - settings.cpp + connection.cpp accesspoint.cpp wireless.cpp utils.cpp - dbus_types.cpp enums.hpp ${NM_DBUS_INTERFACES} ) diff --git a/src/network/nm/accesspoint.hpp b/src/network/nm/accesspoint.hpp index 63e35ee..8409089 100644 --- a/src/network/nm/accesspoint.hpp +++ b/src/network/nm/accesspoint.hpp @@ -48,14 +48,14 @@ public: [[nodiscard]] bool isValid() const; [[nodiscard]] QString path() const; [[nodiscard]] QString address() const; - [[nodiscard]] QByteArray ssid() const { return this->bSsid; } - [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; } - [[nodiscard]] NM80211ApFlags::Enum flags() const { return this->bFlags; } - [[nodiscard]] NM80211ApSecurityFlags::Enum wpaFlags() const { return this->bWpaFlags; } - [[nodiscard]] NM80211ApSecurityFlags::Enum rsnFlags() const { return this->bRsnFlags; } - [[nodiscard]] NM80211Mode::Enum mode() const { return this->bMode; } - [[nodiscard]] QBindable bindableSecurity() { return &this->bSecurity; } - [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; } + [[nodiscard]] QByteArray ssid() const { return this->bSsid; }; + [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; }; + [[nodiscard]] NM80211ApFlags::Enum flags() const { return this->bFlags; }; + [[nodiscard]] NM80211ApSecurityFlags::Enum wpaFlags() const { return this->bWpaFlags; }; + [[nodiscard]] NM80211ApSecurityFlags::Enum rsnFlags() const { return this->bRsnFlags; }; + [[nodiscard]] NM80211Mode::Enum mode() const { return this->bMode; }; + [[nodiscard]] QBindable bindableSecurity() { return &this->bSecurity; }; + [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; }; signals: void loaded(); diff --git a/src/network/nm/active_connection.cpp b/src/network/nm/active_connection.cpp deleted file mode 100644 index cab0e52..0000000 --- a/src/network/nm/active_connection.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "active_connection.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "dbus_nm_active_connection.h" -#include "enums.hpp" - -namespace qs::network { -using namespace qs::dbus; - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); -} - -NMActiveConnection::NMActiveConnection(const QString& path, QObject* parent): QObject(parent) { - this->proxy = new DBusNMActiveConnectionProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - if (!this->proxy->isValid()) { - qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path; - return; - } - - // clang-format off - QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::loaded, Qt::SingleShotConnection); - QObject::connect(this->proxy, &DBusNMActiveConnectionProxy::StateChanged, this, &NMActiveConnection::onStateChanged); - // clang-format on - - this->activeConnectionProperties.setInterface(this->proxy); - this->activeConnectionProperties.updateAllViaGetAll(); -} - -void NMActiveConnection::onStateChanged(quint32 /*state*/, quint32 reason) { - auto enumReason = static_cast(reason); - if (this->bStateReason == enumReason) return; - this->bStateReason = enumReason; -} - -bool NMActiveConnection::isValid() const { return this->proxy && this->proxy->isValid(); } -QString NMActiveConnection::address() const { - return this->proxy ? this->proxy->service() : QString(); -} -QString NMActiveConnection::path() const { return this->proxy ? this->proxy->path() : QString(); } - -} // namespace qs::network - -namespace qs::dbus { - -DBusResult -DBusDataTransform::fromWire(quint32 wire) { - return DBusResult(static_cast(wire)); -} - -} // namespace qs::dbus diff --git a/src/network/nm/backend.cpp b/src/network/nm/backend.cpp index a46ccb2..4b61e33 100644 --- a/src/network/nm/backend.cpp +++ b/src/network/nm/backend.cpp @@ -1,6 +1,5 @@ #include "backend.hpp" -#include #include #include #include @@ -16,7 +15,6 @@ #include "../../core/logcat.hpp" #include "../../dbus/properties.hpp" #include "../device.hpp" -#include "../enums.hpp" #include "../network.hpp" #include "../wifi.hpp" #include "dbus_nm_backend.h" @@ -33,8 +31,7 @@ QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWa } NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) { - qCDebug(logNetworkManager) << "Connecting to NetworkManager"; - qDBusRegisterMetaType(); + qDBusRegisterMetaType(); auto bus = QDBusConnection::systemBus(); if (!bus.isConnected()) { @@ -72,23 +69,6 @@ void NetworkManager::init() { this->registerDevices(); } -void NetworkManager::checkConnectivity() { - auto pending = this->proxy->CheckConnectivity(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [](QDBusPendingCallWatcher* call) { - const QDBusPendingReply reply = *call; - - if (reply.isError()) { - qCInfo(logNetworkManager) << "Failed to check connectivity: " << reply.error().message(); - } - - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - void NetworkManager::registerDevices() { auto pending = this->proxy->GetAllDevices(); auto* call = new QDBusPendingCallWatcher(pending, this); @@ -137,21 +117,23 @@ void NetworkManager::registerDevice(const QString& path) { } if (dev) { - qCDebug(logNetworkManager) << "Device added:" << path; if (!dev->isValid()) { qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; delete dev; } else { this->mDevices[path] = dev; + // Only register a frontend device while it's managed by NM. + auto onManagedChanged = [this, dev, type](bool managed) { + managed ? this->registerFrontendDevice(type, dev) : this->removeFrontendDevice(dev); + }; // clang-format off QObject::connect(dev, &NMDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection); QObject::connect(dev, &NMDevice::activateConnection, this, &NetworkManager::activateConnection); + QObject::connect(dev, &NMDevice::managedChanged, this, onManagedChanged); // clang-format on - this->registerFrontendDevice(type, dev); + if (dev->managed()) this->registerFrontendDevice(type, dev); } - } else { - qCDebug(logNetworkManager) << "Ignoring registration of unsupported device:" << path; } temp->deleteLater(); } @@ -191,22 +173,21 @@ void NetworkManager::registerFrontendDevice(NMDeviceType::Enum type, NMDevice* d // Bind generic NetworkDevice properties auto translateState = [dev]() { switch (dev->state()) { - case 0 ... 20: return ConnectionState::Unknown; - case 30: return ConnectionState::Disconnected; - case 40 ... 90: return ConnectionState::Connecting; - case 100: return ConnectionState::Connected; - case 110 ... 120: return ConnectionState::Disconnecting; + case 0 ... 20: return DeviceConnectionState::Unknown; + case 30: return DeviceConnectionState::Disconnected; + case 40 ... 90: return DeviceConnectionState::Connecting; + case 100: return DeviceConnectionState::Connected; + case 110 ... 120: return DeviceConnectionState::Disconnecting; } }; // clang-format off frontendDev->bindableName().setBinding([dev]() { return dev->interface(); }); frontendDev->bindableAddress().setBinding([dev]() { return dev->hwAddress(); }); + frontendDev->bindableNmState().setBinding([dev]() { return dev->state(); }); frontendDev->bindableState().setBinding(translateState); frontendDev->bindableAutoconnect().setBinding([dev]() { return dev->autoconnect(); }); - frontendDev->bindableNmManaged().setBinding([dev]() { return dev->managed(); }); QObject::connect(frontendDev, &WifiDevice::requestDisconnect, dev, &NMDevice::disconnect); QObject::connect(frontendDev, &NetworkDevice::requestSetAutoconnect, dev, &NMDevice::setAutoconnect); - QObject::connect(frontendDev, &NetworkDevice::requestSetNmManaged, dev, &NMDevice::setManaged); // clang-format on this->mFrontendDevices.insert(dev->path(), frontendDev); @@ -234,7 +215,6 @@ void NetworkManager::onDevicePathRemoved(const QDBusObjectPath& path) { auto* dev = iter.value(); this->mDevices.erase(iter); if (dev) { - qCDebug(logNetworkManager) << "Device removed:" << path.path(); this->removeFrontendDevice(dev); delete dev; } @@ -260,7 +240,7 @@ void NetworkManager::activateConnection( } void NetworkManager::addAndActivateConnection( - const NMSettingsMap& settings, + const ConnectionSettingsMap& settings, const QDBusObjectPath& devPath, const QDBusObjectPath& specificObjectPath ) { @@ -279,12 +259,6 @@ void NetworkManager::addAndActivateConnection( QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); } -void NetworkManager::setConnectivityCheckEnabled(bool enabled) { - if (enabled == this->bConnectivityCheckEnabled) return; - this->bConnectivityCheckEnabled = enabled; - this->pConnectivityCheckEnabled.write(); -} - void NetworkManager::setWifiEnabled(bool enabled) { if (enabled == this->bWifiEnabled) return; this->bWifiEnabled = enabled; @@ -294,12 +268,3 @@ void NetworkManager::setWifiEnabled(bool enabled) { bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); }; } // namespace qs::network - -namespace qs::dbus { - -DBusResult -DBusDataTransform::fromWire(quint32 wire) { - return DBusResult(static_cast(wire)); -} - -} // namespace qs::dbus diff --git a/src/network/nm/backend.hpp b/src/network/nm/backend.hpp index 2825a17..471f57a 100644 --- a/src/network/nm/backend.hpp +++ b/src/network/nm/backend.hpp @@ -10,20 +10,7 @@ #include "../../dbus/properties.hpp" #include "../network.hpp" #include "dbus_nm_backend.h" -#include "dbus_types.hpp" #include "device.hpp" -#include "enums.hpp" - -namespace qs::dbus { - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NMConnectivityState::Enum; - static DBusResult fromWire(Wire wire); -}; - -} // namespace qs::dbus namespace qs::network { @@ -34,34 +21,24 @@ public: explicit NetworkManager(QObject* parent = nullptr); [[nodiscard]] bool isAvailable() const override; - [[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; } - [[nodiscard]] bool wifiHardwareEnabled() const { return this->bWifiHardwareEnabled; } - [[nodiscard]] bool connectivityCheckAvailable() const { - return this->bConnectivityCheckAvailable; - }; - [[nodiscard]] bool connectivityCheckEnabled() const { return this->bConnectivityCheckEnabled; } - [[nodiscard]] NMConnectivityState::Enum connectivity() const { return this->bConnectivity; } + [[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; }; + [[nodiscard]] bool wifiHardwareEnabled() const { return this->bWifiHardwareEnabled; }; signals: void deviceAdded(NetworkDevice* device); void deviceRemoved(NetworkDevice* device); void wifiEnabledChanged(bool enabled); void wifiHardwareEnabledChanged(bool enabled); - void connectivityStateChanged(NMConnectivityState::Enum state); - void connectivityCheckAvailableChanged(bool available); - void connectivityCheckEnabledChanged(bool enabled); public slots: void setWifiEnabled(bool enabled); - void setConnectivityCheckEnabled(bool enabled); - void checkConnectivity(); private slots: void onDevicePathAdded(const QDBusObjectPath& path); void onDevicePathRemoved(const QDBusObjectPath& path); void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath); void addAndActivateConnection( - const NMSettingsMap& settings, + const ConnectionSettingsMap& settings, const QDBusObjectPath& devPath, const QDBusObjectPath& specificObjectPath ); @@ -79,16 +56,10 @@ private: // clang-format off Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiEnabled, &NetworkManager::wifiEnabledChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiHardwareEnabled, &NetworkManager::wifiHardwareEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, NMConnectivityState::Enum, bConnectivity, &NetworkManager::connectivityStateChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bConnectivityCheckAvailable, &NetworkManager::connectivityCheckAvailableChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bConnectivityCheckEnabled, &NetworkManager::connectivityCheckEnabledChanged); QS_DBUS_BINDABLE_PROPERTY_GROUP(NetworkManager, dbusProperties); QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiEnabled, bWifiEnabled, dbusProperties, "WirelessEnabled"); QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiHardwareEnabled, bWifiHardwareEnabled, dbusProperties, "WirelessHardwareEnabled"); - QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivity, bConnectivity, dbusProperties, "Connectivity"); - QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivityCheckAvailable, bConnectivityCheckAvailable, dbusProperties, "ConnectivityCheckAvailable"); - QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivityCheckEnabled, bConnectivityCheckEnabled, dbusProperties, "ConnectivityCheckEnabled"); // clang-format on DBusNetworkManagerProxy* proxy = nullptr; }; diff --git a/src/network/nm/connection.cpp b/src/network/nm/connection.cpp new file mode 100644 index 0000000..39b6f66 --- /dev/null +++ b/src/network/nm/connection.cpp @@ -0,0 +1,151 @@ +#include "connection.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../core/logcat.hpp" +#include "../../dbus/properties.hpp" +#include "../wifi.hpp" +#include "dbus_nm_active_connection.h" +#include "dbus_nm_connection_settings.h" +#include "dbus_types.hpp" +#include "enums.hpp" +#include "utils.hpp" + +namespace qs::network { +using namespace qs::dbus; + +namespace { +QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); +} + +NMConnectionSettings::NMConnectionSettings(const QString& path, QObject* parent): QObject(parent) { + qDBusRegisterMetaType(); + + this->proxy = new DBusNMConnectionSettingsProxy( + "org.freedesktop.NetworkManager", + path, + QDBusConnection::systemBus(), + this + ); + + if (!this->proxy->isValid()) { + qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path; + return; + } + + QObject::connect( + this->proxy, + &DBusNMConnectionSettingsProxy::Updated, + this, + &NMConnectionSettings::updateSettings + ); + this->bSecurity.setBinding([this]() { return securityFromConnectionSettings(this->bSettings); }); + + this->connectionSettingsProperties.setInterface(this->proxy); + this->connectionSettingsProperties.updateAllViaGetAll(); + + this->updateSettings(); +} + +void NMConnectionSettings::updateSettings() { + auto pending = this->proxy->GetSettings(); + auto* call = new QDBusPendingCallWatcher(pending, this); + + auto responseCallback = [this](QDBusPendingCallWatcher* call) { + const QDBusPendingReply reply = *call; + + if (reply.isError()) { + qCWarning(logNetworkManager) + << "Failed to get" << this->path() << "settings:" << reply.error().message(); + } else { + this->bSettings = reply.value(); + } + + if (!this->mLoaded) { + emit this->loaded(); + this->mLoaded = true; + } + delete call; + }; + + QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); +} + +void NMConnectionSettings::forget() { + auto pending = this->proxy->Delete(); + auto* call = new QDBusPendingCallWatcher(pending, this); + + auto responseCallback = [this](QDBusPendingCallWatcher* call) { + const QDBusPendingReply<> reply = *call; + + if (reply.isError()) { + qCWarning(logNetworkManager) + << "Failed to forget" << this->path() << ":" << reply.error().message(); + } + delete call; + }; + + QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); +} + +bool NMConnectionSettings::isValid() const { return this->proxy && this->proxy->isValid(); } +QString NMConnectionSettings::address() const { + return this->proxy ? this->proxy->service() : QString(); +} +QString NMConnectionSettings::path() const { return this->proxy ? this->proxy->path() : QString(); } + +NMActiveConnection::NMActiveConnection(const QString& path, QObject* parent): QObject(parent) { + this->proxy = new DBusNMActiveConnectionProxy( + "org.freedesktop.NetworkManager", + path, + QDBusConnection::systemBus(), + this + ); + + if (!this->proxy->isValid()) { + qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path; + return; + } + + // clang-format off + QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::loaded, Qt::SingleShotConnection); + QObject::connect(this->proxy, &DBusNMActiveConnectionProxy::StateChanged, this, &NMActiveConnection::onStateChanged); + // clang-format on + + this->activeConnectionProperties.setInterface(this->proxy); + this->activeConnectionProperties.updateAllViaGetAll(); +} + +void NMActiveConnection::onStateChanged(quint32 /*state*/, quint32 reason) { + auto enumReason = static_cast(reason); + if (this->mStateReason == enumReason) return; + this->mStateReason = enumReason; + emit this->stateReasonChanged(enumReason); +} + +bool NMActiveConnection::isValid() const { return this->proxy && this->proxy->isValid(); } +QString NMActiveConnection::address() const { + return this->proxy ? this->proxy->service() : QString(); +} +QString NMActiveConnection::path() const { return this->proxy ? this->proxy->path() : QString(); } + +} // namespace qs::network + +namespace qs::dbus { + +DBusResult +DBusDataTransform::fromWire(quint32 wire) { + return DBusResult(static_cast(wire)); +} + +} // namespace qs::dbus diff --git a/src/network/nm/active_connection.hpp b/src/network/nm/connection.hpp similarity index 51% rename from src/network/nm/active_connection.hpp rename to src/network/nm/connection.hpp index 33426a1..4f126c8 100644 --- a/src/network/nm/active_connection.hpp +++ b/src/network/nm/connection.hpp @@ -1,16 +1,18 @@ #pragma once -#include #include #include +#include #include #include -#include -#include +#include #include #include "../../dbus/properties.hpp" +#include "../wifi.hpp" #include "dbus_nm_active_connection.h" +#include "dbus_nm_connection_settings.h" +#include "dbus_types.hpp" #include "enums.hpp" namespace qs::dbus { @@ -26,6 +28,40 @@ struct DBusDataTransform { namespace qs::network { +// Proxy of a /org/freedesktop/NetworkManager/Settings/Connection/* object. +class NMConnectionSettings: public QObject { + Q_OBJECT; + +public: + explicit NMConnectionSettings(const QString& path, QObject* parent = nullptr); + + void forget(); + + [[nodiscard]] bool isValid() const; + [[nodiscard]] QString path() const; + [[nodiscard]] QString address() const; + [[nodiscard]] ConnectionSettingsMap settings() const { return this->bSettings; }; + [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; }; + [[nodiscard]] QBindable bindableSecurity() { return &this->bSecurity; }; + +signals: + void loaded(); + void settingsChanged(ConnectionSettingsMap settings); + void securityChanged(WifiSecurityType::Enum security); + void ssidChanged(QString ssid); + +private: + bool mLoaded = false; + void updateSettings(); + // clang-format off + Q_OBJECT_BINDABLE_PROPERTY(NMConnectionSettings, ConnectionSettingsMap, bSettings, &NMConnectionSettings::settingsChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMConnectionSettings, WifiSecurityType::Enum, bSecurity, &NMConnectionSettings::securityChanged); + QS_DBUS_BINDABLE_PROPERTY_GROUP(NMConnectionSettings, connectionSettingsProperties); + // clang-format on + + DBusNMConnectionSettingsProxy* proxy = nullptr; +}; + // Proxy of a /org/freedesktop/NetworkManager/ActiveConnection/* object. class NMActiveConnection: public QObject { Q_OBJECT; @@ -36,27 +72,31 @@ public: [[nodiscard]] bool isValid() const; [[nodiscard]] QString path() const; [[nodiscard]] QString address() const; - [[nodiscard]] QDBusObjectPath connection() const { return this->bConnection; } - [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; } - [[nodiscard]] NMConnectionStateReason::Enum stateReason() const { return this->bStateReason; } + [[nodiscard]] QDBusObjectPath connection() const { return this->bConnection; }; + [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; }; + [[nodiscard]] NMConnectionStateReason::Enum stateReason() const { return this->mStateReason; }; signals: void loaded(); void connectionChanged(QDBusObjectPath path); void stateChanged(NMConnectionState::Enum state); void stateReasonChanged(NMConnectionStateReason::Enum reason); + void uuidChanged(const QString& uuid); private slots: void onStateChanged(quint32 state, quint32 reason); private: + NMConnectionStateReason::Enum mStateReason = NMConnectionStateReason::Unknown; + // clang-format off Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, QDBusObjectPath, bConnection, &NMActiveConnection::connectionChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, QString, bUuid, &NMActiveConnection::uuidChanged); Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, NMConnectionState::Enum, bState, &NMActiveConnection::stateChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, NMConnectionStateReason::Enum, bStateReason, &NMActiveConnection::stateReasonChanged); QS_DBUS_BINDABLE_PROPERTY_GROUP(NMActiveConnection, activeConnectionProperties); QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pConnection, bConnection, activeConnectionProperties, "Connection"); + QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pUuid, bUuid, activeConnectionProperties, "Uuid"); QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pState, bState, activeConnectionProperties, "State"); // clang-format on DBusNMActiveConnectionProxy* proxy = nullptr; diff --git a/src/network/nm/dbus_types.cpp b/src/network/nm/dbus_types.cpp deleted file mode 100644 index e161f11..0000000 --- a/src/network/nm/dbus_types.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "dbus_types.hpp" - -#include -#include -#include -#include -#include -#include - -namespace qs::network { - -const QDBusArgument& operator>>(const QDBusArgument& argument, NMSettingsMap& map) { - argument.beginMap(); - while (!argument.atEnd()) { - argument.beginMapEntry(); - QString groupName; - argument >> groupName; - - QVariantMap group; - argument >> group; - - map.insert(groupName, group); - argument.endMapEntry(); - } - argument.endMap(); - return argument; -} - -const QDBusArgument& operator<<(QDBusArgument& argument, const NMSettingsMap& map) { - argument.beginMap(qMetaTypeId(), qMetaTypeId()); - for (auto it = map.constBegin(); it != map.constEnd(); ++it) { - argument.beginMapEntry(); - argument << it.key(); - argument << it.value(); - argument.endMapEntry(); - } - argument.endMap(); - return argument; -} - -const QDBusArgument& operator>>(const QDBusArgument& argument, NMIPv6Address& addr) { - argument.beginStructure(); - argument >> addr.address >> addr.prefix >> addr.gateway; - argument.endStructure(); - return argument; -} - -const QDBusArgument& operator<<(QDBusArgument& argument, const NMIPv6Address& addr) { - argument.beginStructure(); - argument << addr.address << addr.prefix << addr.gateway; - argument.endStructure(); - return argument; -} - -const QDBusArgument& operator>>(const QDBusArgument& argument, NMIPv6Route& route) { - argument.beginStructure(); - argument >> route.destination >> route.prefix >> route.nexthop >> route.metric; - argument.endStructure(); - return argument; -} - -const QDBusArgument& operator<<(QDBusArgument& argument, const NMIPv6Route& route) { - argument.beginStructure(); - argument << route.destination << route.prefix << route.nexthop << route.metric; - argument.endStructure(); - return argument; -} - -} // namespace qs::network diff --git a/src/network/nm/dbus_types.hpp b/src/network/nm/dbus_types.hpp index bf428e5..dadbcf3 100644 --- a/src/network/nm/dbus_types.hpp +++ b/src/network/nm/dbus_types.hpp @@ -1,40 +1,9 @@ #pragma once -#include -#include +#include #include #include -#include #include -namespace qs::network { - -using NMSettingsMap = QMap; - -const QDBusArgument& operator>>(const QDBusArgument& argument, NMSettingsMap& map); -const QDBusArgument& operator<<(QDBusArgument& argument, const NMSettingsMap& map); - -struct NMIPv6Address { - QByteArray address; - quint32 prefix = 0; - QByteArray gateway; -}; - -const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Address& addr); -const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Address& addr); - -struct NMIPv6Route { - QByteArray destination; - quint32 prefix = 0; - QByteArray nexthop; - quint32 metric = 0; -}; - -const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Route& route); -const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Route& route); - -} // namespace qs::network - -Q_DECLARE_METATYPE(qs::network::NMSettingsMap); -Q_DECLARE_METATYPE(qs::network::NMIPv6Address); -Q_DECLARE_METATYPE(qs::network::NMIPv6Route); +using ConnectionSettingsMap = QMap; +Q_DECLARE_METATYPE(ConnectionSettingsMap); diff --git a/src/network/nm/device.cpp b/src/network/nm/device.cpp index 1f229c8..aad565d 100644 --- a/src/network/nm/device.cpp +++ b/src/network/nm/device.cpp @@ -14,10 +14,9 @@ #include "../../core/logcat.hpp" #include "../../dbus/properties.hpp" -#include "active_connection.hpp" +#include "../device.hpp" +#include "connection.hpp" #include "dbus_nm_device.h" -#include "enums.hpp" -#include "settings.hpp" namespace qs::network { using namespace qs::dbus; @@ -40,29 +39,19 @@ NMDevice::NMDevice(const QString& path, QObject* parent): QObject(parent) { } // clang-format off - QObject::connect(this, &NMDevice::availableSettingsPathsChanged, this, &NMDevice::onAvailableSettingsPathsChanged); + QObject::connect(this, &NMDevice::availableConnectionPathsChanged, this, &NMDevice::onAvailableConnectionPathsChanged); QObject::connect(this, &NMDevice::activeConnectionPathChanged, this, &NMDevice::onActiveConnectionPathChanged); - QObject::connect(this->deviceProxy, &DBusNMDeviceProxy::StateChanged, this, &NMDevice::onStateChanged); // clang-format on this->deviceProperties.setInterface(this->deviceProxy); this->deviceProperties.updateAllViaGetAll(); } -void NMDevice::onStateChanged(quint32 newState, quint32 /*oldState*/, quint32 reason) { - auto enumReason = static_cast(reason); - auto enumNewState = static_cast(newState); - if (enumNewState == NMDeviceState::Failed) this->bLastFailReason = enumReason; - if (this->bStateReason == enumReason) return; - this->bStateReason = enumReason; -} - void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) { const QString stringPath = path.path(); // Remove old active connection if (this->mActiveConnection) { - qCDebug(logNetworkManager) << "Active connection removed:" << this->mActiveConnection->path(); QObject::disconnect(this->mActiveConnection, nullptr, this, nullptr); delete this->mActiveConnection; this->mActiveConnection = nullptr; @@ -75,7 +64,6 @@ void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) { qCWarning(logNetworkManager) << "Ignoring invalid registration of" << stringPath; delete active; } else { - qCDebug(logNetworkManager) << "Active connection added:" << stringPath; this->mActiveConnection = active; QObject::connect( active, @@ -88,44 +76,42 @@ void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) { } } -void NMDevice::onAvailableSettingsPathsChanged(const QList& paths) { +void NMDevice::onAvailableConnectionPathsChanged(const QList& paths) { QSet newPathSet; for (const QDBusObjectPath& path: paths) { newPathSet.insert(path.path()); } - const auto existingPaths = this->mSettings.keys(); + const auto existingPaths = this->mConnections.keys(); const QSet existingPathSet(existingPaths.begin(), existingPaths.end()); - const auto addedSettings = newPathSet - existingPathSet; - const auto removedSettings = existingPathSet - newPathSet; + const auto addedConnections = newPathSet - existingPathSet; + const auto removedConnections = existingPathSet - newPathSet; - for (const QString& path: addedSettings) { - this->registerSettings(path); + for (const QString& path: addedConnections) { + this->registerConnection(path); } - for (const QString& path: removedSettings) { - auto* connection = this->mSettings.take(path); + for (const QString& path: removedConnections) { + auto* connection = this->mConnections.take(path); if (!connection) { qCDebug(logNetworkManager) << "Sent removal signal for" << path << "which is not registered."; } else { - qCDebug(logNetworkManager) << "Connection settings removed:" << path; delete connection; } }; } -void NMDevice::registerSettings(const QString& path) { - auto* settings = new NMSettings(path, this); - if (!settings->isValid()) { +void NMDevice::registerConnection(const QString& path) { + auto* connection = new NMConnectionSettings(path, this); + if (!connection->isValid()) { qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; - delete settings; + delete connection; } else { - qCDebug(logNetworkManager) << "Connection settings added:" << path; - this->mSettings.insert(path, settings); + this->mConnections.insert(path, connection); QObject::connect( - settings, - &NMSettings::loaded, + connection, + &NMConnectionSettings::loaded, this, - [this, settings]() { emit this->settingsLoaded(settings); }, + [this, connection]() { emit this->connectionLoaded(connection); }, Qt::SingleShotConnection ); } @@ -139,12 +125,6 @@ void NMDevice::setAutoconnect(bool autoconnect) { this->pAutoconnect.write(); } -void NMDevice::setManaged(bool managed) { - if (managed == this->bManaged) return; - this->bManaged = managed; - this->pManaged.write(); -} - bool NMDevice::isValid() const { return this->deviceProxy && this->deviceProxy->isValid(); } QString NMDevice::address() const { return this->deviceProxy ? this->deviceProxy->service() : QString(); diff --git a/src/network/nm/device.hpp b/src/network/nm/device.hpp index 963f574..e3ff4b9 100644 --- a/src/network/nm/device.hpp +++ b/src/network/nm/device.hpp @@ -8,10 +8,8 @@ #include #include "../../dbus/properties.hpp" -#include "../enums.hpp" -#include "active_connection.hpp" +#include "connection.hpp" #include "dbus_nm_device.h" -#include "settings.hpp" namespace qs::dbus { @@ -38,49 +36,43 @@ public: [[nodiscard]] virtual bool isValid() const; [[nodiscard]] QString path() const; [[nodiscard]] QString address() const; - [[nodiscard]] QString interface() const { return this->bInterface; } - [[nodiscard]] QString hwAddress() const { return this->bHwAddress; } - [[nodiscard]] bool managed() const { return this->bManaged; } - [[nodiscard]] NMDeviceState::Enum state() const { return this->bState; } - [[nodiscard]] NMDeviceStateReason::Enum stateReason() const { return this->bStateReason; } - [[nodiscard]] NMDeviceStateReason::Enum lastFailReason() const { return this->bLastFailReason; } - [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; } - [[nodiscard]] NMActiveConnection* activeConnection() const { return this->mActiveConnection; } + [[nodiscard]] QString interface() const { return this->bInterface; }; + [[nodiscard]] QString hwAddress() const { return this->bHwAddress; }; + [[nodiscard]] bool managed() const { return this->bManaged; }; + [[nodiscard]] NMDeviceState::Enum state() const { return this->bState; }; + [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; }; + [[nodiscard]] NMActiveConnection* activeConnection() const { return this->mActiveConnection; }; signals: void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath); void addAndActivateConnection( - const NMSettingsMap& settings, + const ConnectionSettingsMap& settings, const QDBusObjectPath& devPath, const QDBusObjectPath& apPath ); - void settingsLoaded(NMSettings* settings); - void settingsRemoved(NMSettings* settings); - void availableSettingsPathsChanged(QList paths); + void connectionLoaded(NMConnectionSettings* connection); + void connectionRemoved(NMConnectionSettings* connection); + void availableConnectionPathsChanged(QList paths); void activeConnectionPathChanged(const QDBusObjectPath& connection); void activeConnectionLoaded(NMActiveConnection* active); void interfaceChanged(const QString& interface); void hwAddressChanged(const QString& hwAddress); void managedChanged(bool managed); void stateChanged(NMDeviceState::Enum state); - void stateReasonChanged(NMDeviceStateReason::Enum reason); - void lastFailReasonChanged(NMDeviceStateReason::Enum reason); void autoconnectChanged(bool autoconnect); public slots: void disconnect(); void setAutoconnect(bool autoconnect); - void setManaged(bool managed); private slots: - void onStateChanged(quint32 newState, quint32 oldState, quint32 reason); - void onAvailableSettingsPathsChanged(const QList& paths); + void onAvailableConnectionPathsChanged(const QList& paths); void onActiveConnectionPathChanged(const QDBusObjectPath& path); private: - void registerSettings(const QString& path); + void registerConnection(const QString& path); - QHash mSettings; + QHash mConnections; NMActiveConnection* mActiveConnection = nullptr; // clang-format off @@ -88,10 +80,8 @@ private: Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QString, bHwAddress, &NMDevice::hwAddressChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bManaged, &NMDevice::managedChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceState::Enum, bState, &NMDevice::stateChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceStateReason::Enum, bStateReason, &NMDevice::stateReasonChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceStateReason::Enum, bLastFailReason, &NMDevice::lastFailReasonChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bAutoconnect, &NMDevice::autoconnectChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QList, bAvailableConnections, &NMDevice::availableSettingsPathsChanged); + Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QList, bAvailableConnections, &NMDevice::availableConnectionPathsChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QDBusObjectPath, bActiveConnection, &NMDevice::activeConnectionPathChanged); QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDeviceAdapter, deviceProperties); diff --git a/src/network/nm/enums.hpp b/src/network/nm/enums.hpp index 18b1b8b..34e5b65 100644 --- a/src/network/nm/enums.hpp +++ b/src/network/nm/enums.hpp @@ -7,20 +7,6 @@ namespace qs::network { -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMConnectivityState -class NMConnectivityState: public QObject { - Q_OBJECT; - -public: - enum Enum : quint8 { - Unknown = 0, - None = 1, - Portal = 2, - Limited = 3, - Full = 4, - }; -}; - // Indicates the type of hardware represented by a device object. class NMDeviceType: public QObject { Q_OBJECT; @@ -66,123 +52,6 @@ public: Q_ENUM(Enum); }; -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceState. -class NMDeviceState: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - Unknown = 0, - Unmanaged = 10, - Unavailable = 20, - Disconnected = 30, - Prepare = 40, - Config = 50, - NeedAuth = 60, - IPConfig = 70, - IPCheck = 80, - Secondaries = 90, - Activated = 100, - Deactivating = 110, - Failed = 120, - }; - Q_ENUM(Enum); -}; - -// Device state change reason codes. -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceStateReason. -class NMDeviceStateReason: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - None = 0, - Unknown = 1, - NowManaged = 2, - NowUnmanaged = 3, - ConfigFailed = 4, - IpConfigUnavailable = 5, - IpConfigExpired = 6, - NoSecrets = 7, - SupplicantDisconnect = 8, - SupplicantConfigFailed = 9, - SupplicantFailed = 10, - SupplicantTimeout = 11, - PppStartFailed = 12, - PppDisconnect = 13, - PppFailed = 14, - DhcpStartFailed = 15, - DhcpError = 16, - DhcpFailed = 17, - SharedStartFailed = 18, - SharedFailed = 19, - AutoIpStartFailed = 20, - AutoIpError = 21, - AutoIpFailed = 22, - ModemBusy = 23, - ModemNoDialTone = 24, - ModemNoCarrier = 25, - ModemDialTimeout = 26, - ModemDialFailed = 27, - ModemInitFailed = 28, - GsmApnFailed = 29, - GsmRegistrationNotSearching = 30, - GsmRegistrationDenied = 31, - GsmRegistrationTimeout = 32, - GsmRegistrationFailed = 33, - GsmPinCheckFailed = 34, - FirmwareMissing = 35, - Removed = 36, - Sleeping = 37, - ConnectionRemoved = 38, - UserRequested = 39, - Carrier = 40, - ConnectionAssumed = 41, - SupplicantAvailable = 42, - ModemNotFound = 43, - BtFailed = 44, - GsmSimNotInserted = 45, - GsmSimPinRequired = 46, - GsmSimPukRequired = 47, - GsmSimWrong = 48, - InfinibandMode = 49, - DependencyFailed = 50, - Br2684Failed = 51, - ModemManagerUnavailable = 52, - SsidNotFound = 53, - SecondaryConnectionFailed = 54, - DcbFcoeFailed = 55, - TeamdControlFailed = 56, - ModemFailed = 57, - ModemAvailable = 58, - SimPinIncorrect = 59, - NewActivation = 60, - ParentChanged = 61, - ParentManagedChanged = 62, - OvsdbFailed = 63, - IpAddressDuplicate = 64, - IpMethodUnsupported = 65, - SriovConfigurationFailed = 66, - PeerNotFound = 67, - DeviceHandlerFailed = 68, - UnmanagedByDefault = 69, - UnmanagedExternalDown = 70, - UnmanagedLinkNotInit = 71, - UnmanagedQuitting = 72, - UnmanagedManagerDisabled = 73, - UnmanagedUserConf = 74, - UnmanagedUserExplicit = 75, - UnmanagedUserSettings = 76, - UnmanagedUserUdev = 77, - NetworkingOff = 78, - }; - Q_ENUM(Enum); -}; - // 802.11 specific device encryption and authentication capabilities. // In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceWifiCapabilities. class NMWirelessCapabilities: public QObject { @@ -284,31 +153,4 @@ public: Q_ENUM(Enum); }; -/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason. -class NMConnectionStateReason: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_SINGLETON; - -public: - enum Enum : quint8 { - Unknown = 0, - None = 1, - UserDisconnected = 2, - DeviceDisconnected = 3, - ServiceStopped = 4, - IpConfigInvalid = 5, - ConnectTimeout = 6, - ServiceStartTimeout = 7, - ServiceStartFailed = 8, - NoSecrets = 9, - LoginFailed = 10, - ConnectionRemoved = 11, - DependencyFailed = 12, - DeviceRealizeFailed = 13, - DeviceRemoved = 14 - }; - Q_ENUM(Enum); -}; - } // namespace qs::network diff --git a/src/network/nm/org.freedesktop.NetworkManager.Device.xml b/src/network/nm/org.freedesktop.NetworkManager.Device.xml index 414d24f..322635f 100644 --- a/src/network/nm/org.freedesktop.NetworkManager.Device.xml +++ b/src/network/nm/org.freedesktop.NetworkManager.Device.xml @@ -1,10 +1,5 @@ - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml b/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml index 81419b9..0283847 100644 --- a/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml +++ b/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml @@ -2,18 +2,8 @@ - + - - - - - - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.xml b/src/network/nm/org.freedesktop.NetworkManager.xml index 75c314a..d4470ea 100644 --- a/src/network/nm/org.freedesktop.NetworkManager.xml +++ b/src/network/nm/org.freedesktop.NetworkManager.xml @@ -1,8 +1,5 @@ - - - @@ -14,7 +11,7 @@ - + diff --git a/src/network/nm/settings.cpp b/src/network/nm/settings.cpp deleted file mode 100644 index af36dae..0000000 --- a/src/network/nm/settings.cpp +++ /dev/null @@ -1,227 +0,0 @@ -#include "settings.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "dbus_nm_connection_settings.h" -#include "dbus_types.hpp" -#include "utils.hpp" - -namespace qs::network { -using namespace qs::dbus; - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network", QtWarningMsg); -QS_LOGGING_CATEGORY(logNMSettings, "quickshell.network.nm_settings", QtWarningMsg); -} // namespace - -NMSettings::NMSettings(const QString& path, QObject* parent): QObject(parent) { - qDBusRegisterMetaType>(); - qDBusRegisterMetaType>(); - qDBusRegisterMetaType>>(); - qDBusRegisterMetaType>(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType>(); - qDBusRegisterMetaType(); - qDBusRegisterMetaType>(); - - this->proxy = new DBusNMConnectionSettingsProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - if (!this->proxy->isValid()) { - qCWarning(logNetworkManager) << "Cannot create DBus interface for connection settings at" - << path; - return; - } - - QObject::connect( - this->proxy, - &DBusNMConnectionSettingsProxy::Updated, - this, - &NMSettings::getSettings - ); - - this->bId.setBinding([this]() { return this->bSettings.value()["connection"]["id"].toString(); }); - this->bUuid.setBinding([this]() { - return this->bSettings.value()["connection"]["uuid"].toString(); - }); - - this->settingsProperties.setInterface(this->proxy); - this->settingsProperties.updateAllViaGetAll(); - - this->getSettings(); -} - -void NMSettings::getSettings() { - auto pending = this->proxy->GetSettings(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to get settings for" << this->path() << ":" << reply.error().message(); - } else { - auto settings = reply.value(); - manualSettingDemarshall(settings); - this->bSettings = settings; - qCDebug(logNetworkManager) << "Settings map updated for" << this->path(); - - if (!this->mLoaded) { - emit this->loaded(); - this->mLoaded = true; - } - }; - - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -QDBusPendingCallWatcher* NMSettings::updateSettings( - const NMSettingsMap& settingsToChange, - const NMSettingsMap& settingsToRemove -) { - auto settings = removeSettingsInMap(this->bSettings, settingsToRemove); - settings = mergeSettingsMaps(settings, settingsToChange); - auto pending = this->proxy->Update(settings); - auto* call = new QDBusPendingCallWatcher(pending, this); - - return call; -} - -void NMSettings::clearSecrets() { - auto pending = this->proxy->ClearSecrets(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply<> reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to clear secrets for" << this->path() << ":" << reply.error().message(); - } else { - qCDebug(logNetworkManager) << "Cleared secrets for" << this->path(); - } - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -void NMSettings::forget() { - auto pending = this->proxy->Delete(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply<> reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to forget" << this->path() << ":" << reply.error().message(); - } else { - qCDebug(logNetworkManager) << "Successfully deletion of" << this->path(); - } - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -QVariantMap NMSettings::read() { - QVariantMap result; - const auto& settings = this->bSettings.value(); - for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) { - QVariantMap group; - for (auto jt = it.value().constBegin(); jt != it.value().constEnd(); ++jt) { - group.insert(jt.key(), settingTypeToQml(jt.value())); - } - result.insert(it.key(), group); - } - return result; -} - -void NMSettings::write(const QVariantMap& settings) { - NMSettingsMap changedSettings; - NMSettingsMap removedSettings; - QStringList failedSettings; - - for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) { - if (!it.value().canConvert()) continue; - - auto group = it.value().toMap(); - QVariantMap toChange; - QVariantMap toRemove; - for (auto jt = group.constBegin(); jt != group.constEnd(); ++jt) { - if (jt.value().isNull()) { - toRemove.insert(jt.key(), QVariant()); - } else { - auto converted = settingTypeFromQml(it.key(), jt.key(), jt.value()); - if (!converted.isValid()) failedSettings.append(it.key() + "." + jt.key()); - else toChange.insert(jt.key(), converted); - } - } - if (!toChange.isEmpty()) changedSettings.insert(it.key(), toChange); - if (!toRemove.isEmpty()) removedSettings.insert(it.key(), toRemove); - } - - if (!failedSettings.isEmpty()) { - qCWarning(logNMSettings) << "A write to" << this - << "has received bad types for the following settings:" - << failedSettings.join(", "); - } - - auto* call = this->updateSettings(changedSettings, removedSettings); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply<> reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to update settings for" << this->path() << ":" << reply.error().message(); - } else { - qCDebug(logNMSettings) << "Successful write to" << this; - } - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -bool NMSettings::isValid() const { return this->proxy && this->proxy->isValid(); } -QString NMSettings::address() const { return this->proxy ? this->proxy->service() : QString(); } -QString NMSettings::path() const { return this->proxy ? this->proxy->path() : QString(); } - -} // namespace qs::network - -QDebug operator<<(QDebug debug, const qs::network::NMSettings* settings) { - auto saver = QDebugStateSaver(debug); - - if (settings) { - debug.nospace() << "NMSettings(" << static_cast(settings) - << ", uuid=" << settings->uuid() << ")"; - } else { - debug << "WifiNetwork(nullptr)"; - } - - return debug; -} diff --git a/src/network/nm/settings.hpp b/src/network/nm/settings.hpp deleted file mode 100644 index 3a76c61..0000000 --- a/src/network/nm/settings.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../dbus/properties.hpp" -#include "dbus_nm_connection_settings.h" -#include "dbus_types.hpp" - -namespace qs::network { - -// Proxy of a /org/freedesktop/NetworkManager/Settings/Connection/* object. -///! A NetworkManager connection settings profile. -class NMSettings: public QObject { - Q_OBJECT; - QML_ELEMENT; - QML_UNCREATABLE(""); - - /// The human-readable unique identifier for the connection. - Q_PROPERTY(QString id READ default NOTIFY idChanged BINDABLE bindableId); - /// A universally unique identifier for the connection. - Q_PROPERTY(QString uuid READ uuid NOTIFY uuidChanged BINDABLE bindableUuid); - -public: - explicit NMSettings(const QString& path, QObject* parent = nullptr); - - /// Clear all of the secrets belonging to the settings. - Q_INVOKABLE void clearSecrets(); - /// Delete the settings. - Q_INVOKABLE void forget(); - /// Update the connection with new settings and save the connection to disk. - /// Only changed fields need to be included. - /// Writing a setting to `null` will remove the setting or reset it to its default. - /// - /// > [!NOTE] Secrets may be part of the update request, - /// > and will be either stored in persistent storage or sent to a Secret Agent for storage, - /// > depending on the flags associated with each secret. - Q_INVOKABLE void write(const QVariantMap& settings); - /// Get the settings map describing this network configuration. - /// - /// > [!NOTE] This will never include any secrets required for connection to the network, as those are often protected. - Q_INVOKABLE QVariantMap read(); - - [[nodiscard]] bool isValid() const; - [[nodiscard]] QString path() const; - [[nodiscard]] QString address() const; - [[nodiscard]] NMSettingsMap map() { return this->bSettings; } - QDBusPendingCallWatcher* - updateSettings(const NMSettingsMap& settingsToChange, const NMSettingsMap& settingsToRemove = {}); - QBindable bindableId() { return &this->bId; } - [[nodiscard]] QString uuid() const { return this->bUuid; } - QBindable bindableUuid() { return &this->bUuid; } - -signals: - void loaded(); - void settingsChanged(NMSettingsMap settings); - void idChanged(QString id); - void uuidChanged(QString uuid); - -private: - bool mLoaded = false; - - void getSettings(); - - Q_OBJECT_BINDABLE_PROPERTY(NMSettings, NMSettingsMap, bSettings, &NMSettings::settingsChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMSettings, QString, bId, &NMSettings::idChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMSettings, QString, bUuid, &NMSettings::uuidChanged); - QS_DBUS_BINDABLE_PROPERTY_GROUP(NMSettings, settingsProperties); - DBusNMConnectionSettingsProxy* proxy = nullptr; -}; - -} // namespace qs::network - -QDebug operator<<(QDebug debug, const qs::network::NMSettings* settings); diff --git a/src/network/nm/utils.cpp b/src/network/nm/utils.cpp index afdc796..0be29e5 100644 --- a/src/network/nm/utils.cpp +++ b/src/network/nm/utils.cpp @@ -1,28 +1,27 @@ #include "utils.hpp" -#include + // We depend on non-std Linux extensions that ctime doesn't put in the global namespace // NOLINTNEXTLINE(modernize-deprecated-headers) #include #include #include -#include #include #include #include #include -#include "../enums.hpp" +#include "../wifi.hpp" #include "dbus_types.hpp" #include "enums.hpp" namespace qs::network { -WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings) { - const QString mapName = "802-11-wireless-security"; - if (!settings.contains(mapName)) return WifiSecurityType::Unknown; - const QVariantMap& security = settings.value(mapName); - if (security.isEmpty()) return WifiSecurityType::Open; +WifiSecurityType::Enum securityFromConnectionSettings(const ConnectionSettingsMap& settings) { + const QVariantMap& security = settings.value("802-11-wireless-security"); + if (security.isEmpty()) { + return WifiSecurityType::Open; + }; const QString keyMgmt = security["key-mgmt"].toString(); const QString authAlg = security["auth-alg"].toString(); @@ -46,8 +45,6 @@ WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings) { return WifiSecurityType::Sae; } else if (keyMgmt == "wpa-eap-suite-b-192") { return WifiSecurityType::Wpa3SuiteB192; - } else if (keyMgmt == "owe") { - return WifiSecurityType::Owe; } return WifiSecurityType::Open; } @@ -227,280 +224,6 @@ WifiSecurityType::Enum findBestWirelessSecurity( return WifiSecurityType::Unknown; } -NMSettingsMap mergeSettingsMaps(const NMSettingsMap& target, const NMSettingsMap& source) { - NMSettingsMap result = target; - for (auto iter = source.constBegin(); iter != source.constEnd(); ++iter) { - result[iter.key()].insert(iter.value()); - } - return result; -} - -NMSettingsMap removeSettingsInMap(const NMSettingsMap& target, const NMSettingsMap& toRemove) { - NMSettingsMap result = target; - for (auto iter = toRemove.constBegin(); iter != toRemove.constEnd(); ++iter) { - const QString& group = iter.key(); - const QVariantMap& keysToRemove = iter.value(); - - if (!result.contains(group)) continue; - - for (auto jt = keysToRemove.constBegin(); jt != keysToRemove.constEnd(); ++jt) { - result[group].remove(jt.key()); - } - - // Remove the group entirely if it's now empty - if (result[group].isEmpty()) { - result.remove(group); - } - } - return result; -} - -// Some NMSettingsMap settings remain QDBusArguments after autodemarshalling. -// Manually demarshall these for any complex signature we have registered. -void manualSettingDemarshall(NMSettingsMap& map) { - auto demarshallValue = [](const QVariant& value) -> QVariant { - if (value.userType() != qMetaTypeId()) { - return value; - } - - auto arg = value.value(); - auto signature = arg.currentSignature(); - - if (signature == "ay") return QVariant::fromValue(qdbus_cast(arg)); - if (signature == "aay") return QVariant::fromValue(qdbus_cast>(arg)); - if (signature == "au") return QVariant::fromValue(qdbus_cast>(arg)); - if (signature == "aau") return QVariant::fromValue(qdbus_cast>>(arg)); - if (signature == "aa{sv}") return QVariant::fromValue(qdbus_cast>(arg)); - if (signature == "a(ayuay)") return QVariant::fromValue(qdbus_cast>(arg)); - if (signature == "a(ayuayu)") return QVariant::fromValue(qdbus_cast>(arg)); - - return value; - }; - - for (auto it = map.begin(); it != map.end(); ++it) - for (auto jt = it.value().begin(); jt != it.value().end(); ++jt) - jt.value() = demarshallValue(jt.value()); -} - -// Some NMSettingsMap setting types can't be expressed in QML. -// Convert these settings to their correct type or return an invalid QVariant. -QVariant settingTypeFromQml(const QString& group, const QString& key, const QVariant& value) { - auto s = group + "." + key; - - // QString -> QByteArray - if (s == "802-1x.ca-cert" || s == "802-1x.client-cert" || s == "802-1x.private-key" - || s == "802-1x.password-raw" || s == "802-1x.phase2-ca-cert" - || s == "802-1x.phase2-client-cert" || s == "802-1x.phase2-private-key" - || s == "802-11-wireless.ssid") - { - if (value.typeId() == QMetaType::QString) { - return value.toString().toUtf8(); - } - if (value.typeId() == QMetaType::QByteArray) { - return value; - } - return QVariant(); - } - - // QVariantList -> QList - if (s == "ipv6.dns") { - if (value.typeId() == QMetaType::QVariantList) { - QList r; - for (const auto& v: value.toList()) { - if (v.typeId() == QMetaType::QString) { - r.append(v.toString().toUtf8()); - } else { - r.append(v.toByteArray()); - } - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QList - if (s == "ipv4.dns") { - if (value.typeId() == QMetaType::QVariantList) { - QList r; - for (const auto& v: value.toList()) { - r.append(v.value()); - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QList> - if (s == "ipv4.addresses" || s == "ipv4.routes") { - if (value.typeId() == QMetaType::QVariantList) { - QList> r; - for (const auto& v: value.toList()) { - if (v.typeId() != QMetaType::QVariantList) { - continue; - } - QList inner; - for (const auto& u: v.toList()) { - inner.append(u.value()); - } - r.append(inner); - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QList - if (s == "ipv4.address-data" || s == "ipv4.route-data" || s == "ipv4.routing-rules" - || s == "ipv6.address-data" || s == "ipv6.route-data" || s == "ipv6.routing-rules") - { - if (value.typeId() == QMetaType::QVariantList) { - QList r; - for (const auto& v: value.toList()) { - if (!v.canConvert()) { - continue; - } - r.append(v.toMap()); - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QList - if (s == "ipv6.addresses") { - if (value.typeId() == QMetaType::QVariantList) { - QList r; - for (const auto& v: value.toList()) { - if (v.typeId() != QMetaType::QVariantList) { - continue; - } - auto fields = v.toList(); - if (fields.size() != 3) { - continue; - } - const QByteArray address = fields[0].typeId() == QMetaType::QString - ? fields[0].toString().toUtf8() - : fields[0].toByteArray(); - const QByteArray gateway = fields[2].typeId() == QMetaType::QString - ? fields[2].toString().toUtf8() - : fields[2].toByteArray(); - r.append({.address = address, .prefix = fields[1].value(), .gateway = gateway}); - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QList - if (s == "ipv6.routes") { - if (value.typeId() == QMetaType::QVariantList) { - QList r; - for (const auto& v: value.toList()) { - if (v.typeId() != QMetaType::QVariantList) { - continue; - } - auto fields = v.toList(); - if (fields.size() != 4) { - continue; - } - const QByteArray destination = fields[0].typeId() == QMetaType::QString - ? fields[0].toString().toUtf8() - : fields[0].toByteArray(); - const QByteArray nexthop = fields[2].typeId() == QMetaType::QString - ? fields[2].toString().toUtf8() - : fields[2].toByteArray(); - r.append( - {.destination = destination, - .prefix = fields[1].value(), - .nexthop = nexthop, - .metric = fields[3].value()} - ); - } - return QVariant::fromValue(r); - } - return QVariant(); - } - - // QVariantList -> QStringList - if (s == "connection.permissions" || s == "ipv4.dns-search" || s == "ipv6.dns-search" - || s == "802-11-wireless.seen-bssids") - { - if (value.typeId() == QMetaType::QVariantList) { - QStringList stringList; - for (const auto& item: value.toList()) { - stringList.append(item.toString()); - } - return stringList; - } - return QVariant(); - } - - // double (whole number) -> qint32 - if (value.typeId() == QMetaType::Double) { - auto num = value.toDouble(); - if (std::isfinite(num) && num == std::trunc(num)) { - return QVariant::fromValue(static_cast(num)); - } - } - - return value; -} - -// Some NMSettingsMap setting types must be converted to a type that is supported by QML. -// Although QByteArrays can be represented in QML, we convert them to strings for convenience. -QVariant settingTypeToQml(const QVariant& value) { - // QByteArray -> QString - if (value.typeId() == QMetaType::QByteArray) { - return QString::fromUtf8(value.toByteArray()); - } - - // QList -> QVariantList - if (value.userType() == qMetaTypeId>()) { - QVariantList out; - for (const auto& ba: value.value>()) { - out.append(QString::fromUtf8(ba)); - } - return out; - } - - // QList -> QVariantList - if (value.userType() == qMetaTypeId>()) { - QVariantList out; - for (const auto& addr: value.value>()) { - out.append( - QVariant::fromValue( - QVariantList { - QString::fromUtf8(addr.address), - addr.prefix, - QString::fromUtf8(addr.gateway), - } - ) - ); - } - return out; - } - - // QList -> QVariantList - if (value.userType() == qMetaTypeId>()) { - QVariantList out; - for (const auto& route: value.value>()) { - out.append( - QVariant::fromValue( - QVariantList { - QString::fromUtf8(route.destination), - route.prefix, - QString::fromUtf8(route.nexthop), - route.metric, - } - ) - ); - } - return out; - } - - return value; -} - // NOLINTBEGIN QDateTime clockBootTimeToDateTime(qint64 clockBootTime) { clockid_t clkId = CLOCK_BOOTTIME; diff --git a/src/network/nm/utils.hpp b/src/network/nm/utils.hpp index 8c51423..ce8b784 100644 --- a/src/network/nm/utils.hpp +++ b/src/network/nm/utils.hpp @@ -3,14 +3,15 @@ #include #include #include +#include -#include "../enums.hpp" +#include "../wifi.hpp" #include "dbus_types.hpp" #include "enums.hpp" namespace qs::network { -WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings); +WifiSecurityType::Enum securityFromConnectionSettings(const ConnectionSettingsMap& settings); bool deviceSupportsApCiphers( NMWirelessCapabilities::Enum caps, @@ -39,16 +40,6 @@ WifiSecurityType::Enum findBestWirelessSecurity( NM80211ApSecurityFlags::Enum apRsn ); -NMSettingsMap mergeSettingsMaps(const NMSettingsMap& target, const NMSettingsMap& source); - -NMSettingsMap removeSettingsInMap(const NMSettingsMap& target, const NMSettingsMap& toRemove); - -void manualSettingDemarshall(NMSettingsMap& map); - -QVariant settingTypeFromQml(const QString& group, const QString& key, const QVariant& value); - -QVariant settingTypeToQml(const QVariant& value); - QDateTime clockBootTimeToDateTime(qint64 clockBootTime); } // namespace qs::network diff --git a/src/network/nm/wireless.cpp b/src/network/nm/wireless.cpp index 5f55bed..9dff14b 100644 --- a/src/network/nm/wireless.cpp +++ b/src/network/nm/wireless.cpp @@ -1,7 +1,6 @@ #include "wireless.hpp" #include -#include #include #include #include @@ -12,23 +11,20 @@ #include #include #include -#include #include #include #include -#include #include "../../core/logcat.hpp" #include "../../dbus/properties.hpp" -#include "../enums.hpp" +#include "../network.hpp" #include "../wifi.hpp" #include "accesspoint.hpp" -#include "active_connection.hpp" +#include "connection.hpp" #include "dbus_nm_wireless.h" #include "dbus_types.hpp" #include "device.hpp" #include "enums.hpp" -#include "settings.hpp" #include "utils.hpp" namespace qs::network { @@ -46,43 +42,38 @@ NMWirelessNetwork::NMWirelessNetwork(QString ssid, QObject* parent) , bReason(NMConnectionStateReason::None) , bState(NMConnectionState::Deactivated) {} -void NMWirelessNetwork::updateReferenceSettings() { +void NMWirelessNetwork::updateReferenceConnection() { // If the network has no connections, the reference is nullptr. - if (this->mSettings.isEmpty()) { - this->mReferenceSettings = nullptr; + if (this->mConnections.isEmpty()) { + this->mReferenceConn = nullptr; this->bSecurity = WifiSecurityType::Unknown; + // Set security back to reference AP. if (this->mReferenceAp) { this->bSecurity.setBinding([this]() { return this->mReferenceAp->security(); }); } return; }; - // If the network has an active connection, use its settings as the reference. + // If the network has an active connection, use it as the reference. if (this->mActiveConnection) { - auto* settings = this->mSettings.value(this->mActiveConnection->connection().path()); - if (settings && settings != this->mReferenceSettings) { - this->mReferenceSettings = settings; - this->bSecurity.setBinding([settings]() { return securityFromSettingsMap(settings->map()); }); + auto* conn = this->mConnections.value(this->mActiveConnection->connection().path()); + if (conn && conn != this->mReferenceConn) { + this->mReferenceConn = conn; + this->bSecurity.setBinding([conn]() { return conn->security(); }); } return; } - // Otherwise, choose the settings responsible for the last successful connection. - NMSettings* selectedSettings = nullptr; - quint64 selectedTimestamp = 0; - for (auto* settings: this->mSettings.values()) { - const quint64 timestamp = settings->map()["connection"]["timestamp"].toULongLong(); - if (!selectedSettings || timestamp > selectedTimestamp) { - selectedSettings = settings; - selectedTimestamp = timestamp; + // Otherwise, choose the connection with the strongest security settings. + NMConnectionSettings* selectedConn = nullptr; + for (auto* conn: this->mConnections.values()) { + if (!selectedConn || conn->security() > selectedConn->security()) { + selectedConn = conn; } } - - if (this->mReferenceSettings != selectedSettings) { - this->mReferenceSettings = selectedSettings; - this->bSecurity.setBinding([selectedSettings]() { - return securityFromSettingsMap(selectedSettings->map()); - }); + if (this->mReferenceConn != selectedConn) { + this->mReferenceConn = selectedConn; + this->bSecurity.setBinding([selectedConn]() { return selectedConn->security(); }); } } @@ -110,7 +101,7 @@ void NMWirelessNetwork::updateReferenceAp() { this->mReferenceAp = selectedAp; this->bSignalStrength.setBinding([selectedAp]() { return selectedAp->signalStrength(); }); // Reference AP is used for security when there's no connection settings. - if (!this->mReferenceSettings) { + if (!this->mReferenceConn) { this->bSecurity.setBinding([selectedAp]() { return selectedAp->security(); }); } } @@ -122,7 +113,7 @@ void NMWirelessNetwork::addAccessPoint(NMAccessPoint* ap) { auto onDestroyed = [this, ap]() { if (this->mAccessPoints.take(ap->path())) { this->updateReferenceAp(); - if (this->mAccessPoints.isEmpty() && this->mSettings.isEmpty()) emit this->disappeared(); + if (this->mAccessPoints.isEmpty() && this->mConnections.isEmpty()) emit this->disappeared(); } }; // clang-format off @@ -132,45 +123,44 @@ void NMWirelessNetwork::addAccessPoint(NMAccessPoint* ap) { this->updateReferenceAp(); }; -void NMWirelessNetwork::addSettings(NMSettings* settings) { - if (this->mSettings.contains(settings->path())) return; - this->mSettings.insert(settings->path(), settings); - - auto onDestroyed = [this, settings]() { - if (this->mSettings.take(settings->path())) { - emit this->settingsRemoved(settings); - this->updateReferenceSettings(); - if (this->mSettings.isEmpty()) this->bKnown = false; - if (this->mAccessPoints.isEmpty() && this->mSettings.isEmpty()) emit this->disappeared(); +void NMWirelessNetwork::addConnection(NMConnectionSettings* conn) { + if (this->mConnections.contains(conn->path())) return; + this->mConnections.insert(conn->path(), conn); + auto onDestroyed = [this, conn]() { + if (this->mConnections.take(conn->path())) { + this->updateReferenceConnection(); + if (this->mConnections.isEmpty()) this->bKnown = false; + if (this->mAccessPoints.isEmpty() && this->mConnections.isEmpty()) emit this->disappeared(); } }; - QObject::connect(settings, &NMSettings::destroyed, this, onDestroyed); + // clang-format off + QObject::connect(conn, &NMConnectionSettings::securityChanged, this, &NMWirelessNetwork::updateReferenceConnection); + QObject::connect(conn, &NMConnectionSettings::destroyed, this, onDestroyed); + // clang-format on this->bKnown = true; - this->updateReferenceSettings(); - emit this->settingsAdded(settings); + this->updateReferenceConnection(); }; void NMWirelessNetwork::addActiveConnection(NMActiveConnection* active) { if (this->mActiveConnection) return; this->mActiveConnection = active; - this->bState.setBinding([active]() { return active->state(); }); this->bReason.setBinding([active]() { return active->stateReason(); }); auto onDestroyed = [this, active]() { if (this->mActiveConnection && this->mActiveConnection == active) { this->mActiveConnection = nullptr; - this->updateReferenceSettings(); + this->updateReferenceConnection(); this->bState = NMConnectionState::Deactivated; this->bReason = NMConnectionStateReason::None; } }; QObject::connect(active, &NMActiveConnection::destroyed, this, onDestroyed); - this->updateReferenceSettings(); + this->updateReferenceConnection(); }; void NMWirelessNetwork::forget() { - if (this->mSettings.isEmpty()) return; - for (auto* conn: this->mSettings.values()) { + if (this->mConnections.isEmpty()) return; + for (auto* conn: this->mConnections.values()) { conn->forget(); } } @@ -210,7 +200,7 @@ void NMWirelessDevice::initWireless() { QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointAdded, this, &NMWirelessDevice::onAccessPointAdded); QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointRemoved, this, &NMWirelessDevice::onAccessPointRemoved); QObject::connect(this, &NMWirelessDevice::accessPointLoaded, this, &NMWirelessDevice::onAccessPointLoaded); - QObject::connect(this, &NMWirelessDevice::settingsLoaded, this, &NMWirelessDevice::onSettingsLoaded); + QObject::connect(this, &NMWirelessDevice::connectionLoaded, this, &NMWirelessDevice::onConnectionLoaded); QObject::connect(this, &NMWirelessDevice::activeConnectionLoaded, this, &NMWirelessDevice::onActiveConnectionLoaded); QObject::connect(this, &NMWirelessDevice::scanningChanged, this, &NMWirelessDevice::onScanningChanged); // clang-format on @@ -228,7 +218,6 @@ void NMWirelessDevice::onAccessPointRemoved(const QDBusObjectPath& path) { << "which is not registered."; return; } - qCDebug(logNetworkManager) << "Access point removed:" << path.path(); delete ap; } @@ -244,26 +233,28 @@ void NMWirelessDevice::onAccessPointLoaded(NMAccessPoint* ap) { } } -void NMWirelessDevice::onSettingsLoaded(NMSettings* settings) { - const NMSettingsMap& map = settings->map(); +void NMWirelessDevice::onConnectionLoaded(NMConnectionSettings* conn) { + const ConnectionSettingsMap& settings = conn->settings(); // Filter connections that aren't wireless or have missing settings - if (map["connection"]["id"].toString().isEmpty() || map["connection"]["uuid"].toString().isEmpty() - || !map.contains("802-11-wireless") || map["802-11-wireless"]["ssid"].toString().isEmpty()) + if (settings["connection"]["id"].toString().isEmpty() + || settings["connection"]["uuid"].toString().isEmpty() + || !settings.contains("802-11-wireless") + || settings["802-11-wireless"]["ssid"].toString().isEmpty()) { return; } - const auto ssid = map["802-11-wireless"]["ssid"].toString(); - const auto mode = map["802-11-wireless"]["mode"].toString(); + const auto ssid = settings["802-11-wireless"]["ssid"].toString(); + const auto mode = settings["802-11-wireless"]["mode"].toString(); if (mode == "infrastructure") { auto* net = this->mNetworks.value(ssid); if (!net) net = this->registerNetwork(ssid); - net->addSettings(settings); + net->addConnection(conn); // Check for active connections that loaded before their respective connection settings auto* active = this->activeConnection(); - if (active && settings->path() == active->connection().path()) { + if (active && conn->path() == active->connection().path()) { net->addActiveConnection(active); } } @@ -274,8 +265,8 @@ void NMWirelessDevice::onActiveConnectionLoaded(NMActiveConnection* active) { // Find an exisiting network with connection settings that matches the active const QString activeConnPath = active->connection().path(); for (const auto& net: this->mNetworks.values()) { - for (auto* settings: net->settings()) { - if (activeConnPath == settings->path()) { + for (auto* conn: net->connections()) { + if (activeConnPath == conn->path()) { net->addActiveConnection(active); return; } @@ -343,7 +334,6 @@ void NMWirelessDevice::registerAccessPoint(const QString& path) { return; } - qCDebug(logNetworkManager) << "Access point added:" << path; this->mAccessPoints.insert(path, ap); QObject::connect( ap, @@ -366,18 +356,22 @@ void NMWirelessDevice::registerAccessPoint(const QString& path) { NMWirelessNetwork* NMWirelessDevice::registerNetwork(const QString& ssid) { auto* net = new NMWirelessNetwork(ssid, this); + // To avoid exposing outdated state to the frontend, filter the backend networks to only show + // the known or currently connected networks when the scanner is off. auto visible = [this, net]() { return this->bScanning || net->state() == NMConnectionState::Activated || net->known(); }; + auto onVisibilityChanged = [this, net](bool visible) { + visible ? this->registerFrontendNetwork(net) : this->removeFrontendNetwork(net); + }; net->bindableVisible().setBinding(visible); net->bindableActiveApPath().setBinding([this]() { return this->activeApPath().path(); }); - net->bindableDeviceFailReason().setBinding([this]() { return this->lastFailReason(); }); QObject::connect(net, &NMWirelessNetwork::disappeared, this, &NMWirelessDevice::removeNetwork); + QObject::connect(net, &NMWirelessNetwork::visibilityChanged, this, onVisibilityChanged); - qCDebug(logNetworkManager) << "Registered network for SSID" << ssid; this->mNetworks.insert(ssid, net); - this->registerFrontendNetwork(net); + if (net->visible()) this->registerFrontendNetwork(net); return net; } @@ -391,137 +385,46 @@ void NMWirelessDevice::registerFrontendNetwork(NMWirelessNetwork* net) { frontendNet->bindableSignalStrength().setBinding(translateSignal); frontendNet->bindableConnected().setBinding(translateState); frontendNet->bindableKnown().setBinding([net]() { return net->known(); }); + frontendNet->bindableNmReason().setBinding([net]() { return net->reason(); }); frontendNet->bindableSecurity().setBinding([net]() { return net->security(); }); frontendNet->bindableState().setBinding([net]() { - return static_cast(net->state()); - }); - - QObject::connect(net, &NMWirelessNetwork::reasonChanged, this, [net, frontendNet]() { - if (net->reason() == NMConnectionStateReason::DeviceDisconnected) { - auto deviceReason = net->deviceFailReason(); - if (deviceReason == NMDeviceStateReason::NoSecrets) - emit frontendNet->connectionFailed(ConnectionFailReason::NoSecrets); - if (deviceReason == NMDeviceStateReason::SupplicantDisconnect) - emit frontendNet->connectionFailed(ConnectionFailReason::WifiClientDisconnected); - if (deviceReason == NMDeviceStateReason::SupplicantFailed) - emit frontendNet->connectionFailed(ConnectionFailReason::WifiClientFailed); - if (deviceReason == NMDeviceStateReason::SupplicantTimeout) - emit frontendNet->connectionFailed(ConnectionFailReason::WifiAuthTimeout); - if (deviceReason == NMDeviceStateReason::SsidNotFound) - emit frontendNet->connectionFailed(ConnectionFailReason::WifiNetworkLost); - } + return static_cast(net->state()); }); QObject::connect(frontendNet, &WifiNetwork::requestConnect, this, [this, net]() { - if (net->referenceSettings()) { + if (net->referenceConnection()) { emit this->activateConnection( - QDBusObjectPath(net->referenceSettings()->path()), + QDBusObjectPath(net->referenceConnection()->path()), QDBusObjectPath(this->path()) ); return; } if (net->referenceAp()) { emit this->addAndActivateConnection( - NMSettingsMap(), + ConnectionSettingsMap(), QDBusObjectPath(this->path()), QDBusObjectPath(net->referenceAp()->path()) ); - return; } - qCInfo(logNetworkManager) << "Failed to connect to" - << this->path() + ": The network disappeared."; }); QObject::connect( frontendNet, - &WifiNetwork::requestConnectWithPsk, + &WifiNetwork::requestDisconnect, this, - [this, net](const QString& psk) { - NMSettingsMap settings; - settings["802-11-wireless-security"]["psk"] = psk; - if (const QPointer ref = net->referenceSettings()) { - auto* call = ref->updateSettings(settings); - QObject::connect( - call, - &QDBusPendingCallWatcher::finished, - this, - [this, ref](QDBusPendingCallWatcher* call) { - const QDBusPendingReply<> reply = *call; - - if (reply.isError()) { - qCInfo(logNetworkManager) - << "Failed to write PSK for" << this->path() + ":" << reply.error().message(); - } else { - if (!ref) { - qCInfo(logNetworkManager) << "Failed to connectWithPsk to" - << this->path() + ": The settings disappeared."; - } else { - emit this->activateConnection( - QDBusObjectPath(ref->path()), - QDBusObjectPath(this->path()) - ); - } - } - delete call; - } - ); - return; - } - if (net->referenceAp()) { - emit this->addAndActivateConnection( - settings, - QDBusObjectPath(this->path()), - QDBusObjectPath(net->referenceAp()->path()) - ); - return; - } - qCInfo(logNetworkManager) << "Failed to connectWithPsk to" - << this->path() + ": The network disappeared."; - } + &NMWirelessDevice::disconnect ); - QObject::connect( - frontendNet, - &WifiNetwork::requestConnectWithSettings, - this, - [this](NMSettings* settings) { - if (settings) { - emit this->activateConnection( - QDBusObjectPath(settings->path()), - QDBusObjectPath(this->path()) - ); - return; - } - qCInfo(logNetworkManager) << "Failed to connectWithSettings to" - << this->path() + ": The provided settings no longer exist."; - } - ); - - QObject::connect( - net, - &NMWirelessNetwork::visibilityChanged, - this, - [this, frontendNet](bool visible) { - if (visible) this->networkAdded(frontendNet); - else this->networkRemoved(frontendNet); - } - ); - - // clang-format off - QObject::connect(frontendNet, &WifiNetwork::requestDisconnect, this, &NMWirelessDevice::disconnect); QObject::connect(frontendNet, &WifiNetwork::requestForget, net, &NMWirelessNetwork::forget); - QObject::connect(net, &NMWirelessNetwork::settingsAdded, frontendNet, &WifiNetwork::settingsAdded); - QObject::connect(net, &NMWirelessNetwork::settingsRemoved, frontendNet, &WifiNetwork::settingsRemoved); - // clang-format on this->mFrontendNetworks.insert(ssid, frontendNet); - if (net->visible()) emit this->networkAdded(frontendNet); + emit this->networkAdded(frontendNet); } void NMWirelessDevice::removeFrontendNetwork(NMWirelessNetwork* net) { auto* frontendNet = this->mFrontendNetworks.take(net->ssid()); if (frontendNet) { - if (net->visible()) emit this->networkRemoved(frontendNet); + emit this->networkRemoved(frontendNet); frontendNet->deleteLater(); } } diff --git a/src/network/nm/wireless.hpp b/src/network/nm/wireless.hpp index 94ce754..fe4010e 100644 --- a/src/network/nm/wireless.hpp +++ b/src/network/nm/wireless.hpp @@ -9,11 +9,10 @@ #include "../wifi.hpp" #include "accesspoint.hpp" -#include "active_connection.hpp" +#include "connection.hpp" #include "dbus_nm_wireless.h" #include "device.hpp" #include "enums.hpp" -#include "settings.hpp" namespace qs::dbus { template <> @@ -33,7 +32,7 @@ struct DBusDataTransform { } // namespace qs::dbus namespace qs::network { -// NMWirelessNetwork aggregates all related NMActiveConnection, NMAccessPoint, and NMSettings objects. +// NMWirelessNetwork aggregates all related NMActiveConnection, NMAccessPoint, and NMConnectionSetting objects. class NMWirelessNetwork: public QObject { Q_OBJECT; @@ -41,51 +40,46 @@ public: explicit NMWirelessNetwork(QString ssid, QObject* parent = nullptr); void addAccessPoint(NMAccessPoint* ap); - void addSettings(NMSettings* settings); + void addConnection(NMConnectionSettings* conn); void addActiveConnection(NMActiveConnection* active); void forget(); - // clang-format off - [[nodiscard]] QString ssid() const { return this->mSsid; } - [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; } - [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; } - [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; } - [[nodiscard]] bool known() const { return this->bKnown; } - [[nodiscard]] NMConnectionStateReason::Enum reason() const { return this->bReason; } - QBindable bindableDeviceFailReason() { return &this->bDeviceFailReason; } - [[nodiscard]] NMDeviceStateReason::Enum deviceFailReason() const { return this->bDeviceFailReason; } - [[nodiscard]] NMAccessPoint* referenceAp() const { return this->mReferenceAp; } - [[nodiscard]] QList accessPoints() const { return this->mAccessPoints.values(); } - [[nodiscard]] QList settings() const { return this->mSettings.values(); } - [[nodiscard]] NMSettings* referenceSettings() const { return this->mReferenceSettings; } - QBindable bindableActiveApPath() { return &this->bActiveApPath; } - QBindable bindableVisible() { return &this->bVisible; } - bool visible() const { return this->bVisible; } - // clang-format on + [[nodiscard]] QString ssid() const { return this->mSsid; }; + [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; }; + [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; }; + [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; }; + [[nodiscard]] bool known() const { return this->bKnown; }; + [[nodiscard]] NMConnectionStateReason::Enum reason() const { return this->bReason; }; + [[nodiscard]] NMAccessPoint* referenceAp() const { return this->mReferenceAp; }; + [[nodiscard]] NMConnectionSettings* referenceConnection() const { return this->mReferenceConn; }; + [[nodiscard]] QList accessPoints() const { return this->mAccessPoints.values(); }; + [[nodiscard]] QList connections() const { + return this->mConnections.values(); + } + [[nodiscard]] QBindable bindableActiveApPath() { return &this->bActiveApPath; }; + [[nodiscard]] QBindable bindableVisible() { return &this->bVisible; }; + [[nodiscard]] bool visible() const { return this->bVisible; }; signals: void disappeared(); - void settingsAdded(NMSettings* settings); - void settingsRemoved(NMSettings* settings); void visibilityChanged(bool visible); void signalStrengthChanged(quint8 signal); void stateChanged(NMConnectionState::Enum state); void knownChanged(bool known); void securityChanged(WifiSecurityType::Enum security); void reasonChanged(NMConnectionStateReason::Enum reason); - void deviceFailReasonChanged(NMDeviceStateReason::Enum reason); void capabilitiesChanged(NMWirelessCapabilities::Enum caps); void activeApPathChanged(QString path); private: void updateReferenceAp(); - void updateReferenceSettings(); + void updateReferenceConnection(); QString mSsid; QHash mAccessPoints; - QHash mSettings; + QHash mConnections; NMAccessPoint* mReferenceAp = nullptr; - NMSettings* mReferenceSettings = nullptr; + NMConnectionSettings* mReferenceConn = nullptr; NMActiveConnection* mActiveConnection = nullptr; // clang-format off @@ -94,7 +88,6 @@ private: Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, WifiSecurityType::Enum, bSecurity, &NMWirelessNetwork::securityChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionStateReason::Enum, bReason, &NMWirelessNetwork::reasonChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionState::Enum, bState, &NMWirelessNetwork::stateChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMDeviceStateReason::Enum, bDeviceFailReason, &NMWirelessNetwork::deviceFailReasonChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, quint8, bSignalStrength, &NMWirelessNetwork::signalStrengthChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, QString, bActiveApPath, &NMWirelessNetwork::activeApPathChanged); // clang-format on @@ -110,10 +103,10 @@ public: explicit NMWirelessDevice(const QString& path, QObject* parent = nullptr); [[nodiscard]] bool isValid() const override; - [[nodiscard]] NMWirelessCapabilities::Enum capabilities() { return this->bCapabilities; } - [[nodiscard]] const QDBusObjectPath& activeApPath() { return this->bActiveAccessPoint; } - [[nodiscard]] NM80211Mode::Enum mode() { return this->bMode; } - [[nodiscard]] QBindable bindableScanning() { return &this->bScanning; } + [[nodiscard]] NMWirelessCapabilities::Enum capabilities() { return this->bCapabilities; }; + [[nodiscard]] const QDBusObjectPath& activeApPath() { return this->bActiveAccessPoint; }; + [[nodiscard]] NM80211Mode::Enum mode() { return this->bMode; }; + [[nodiscard]] QBindable bindableScanning() { return &this->bScanning; }; signals: void accessPointLoaded(NMAccessPoint* ap); @@ -130,7 +123,7 @@ private slots: void onAccessPointAdded(const QDBusObjectPath& path); void onAccessPointRemoved(const QDBusObjectPath& path); void onAccessPointLoaded(NMAccessPoint* ap); - void onSettingsLoaded(NMSettings* settings); + void onConnectionLoaded(NMConnectionSettings* conn); void onActiveConnectionLoaded(NMActiveConnection* active); void onScanTimeout(); void onScanningChanged(bool scanning); diff --git a/src/network/test/manual/network.qml b/src/network/test/manual/network.qml index eadc159..0fd0f72 100644 --- a/src/network/test/manual/network.qml +++ b/src/network/test/manual/network.qml @@ -5,356 +5,151 @@ import Quickshell import Quickshell.Widgets import Quickshell.Networking -Scope { - Component { - id: editorComponent - FloatingWindow { - id: editorWindow - required property var nmSettings - color: contentItem.palette.window +FloatingWindow { + color: contentItem.palette.window - Component.onCompleted: editorArea.text = JSON.stringify(nmSettings.read(), null, 2) + ColumnLayout { + anchors.fill: parent + anchors.margins: 5 - ColumnLayout { - anchors.fill: parent - anchors.margins: 10 + Column { + Layout.fillWidth: true + RowLayout { + Label { + text: "WiFi" + font.bold: true + font.pointSize: 12 + } + CheckBox { + text: "Software" + checked: Networking.wifiEnabled + onClicked: Networking.wifiEnabled = !Networking.wifiEnabled + } + CheckBox { + enabled: false + text: "Hardware" + checked: Networking.wifiHardwareEnabled + } + } + } - Label { - text: "Editing " + nmSettings?.id + " (" + nmSettings?.uuid + ")" - font.bold: true - font.pointSize: 12 - } + ListView { + clip: true + Layout.fillWidth: true + Layout.fillHeight: true + model: Networking.devices - ScrollView { - Layout.fillWidth: true - Layout.fillHeight: true - TextArea { - id: editorArea - wrapMode: TextEdit.Wrap - selectByMouse: true - } - } + delegate: WrapperRectangle { + width: parent.width + color: "transparent" + border.color: palette.button + border.width: 1 + margin: 5 - RowLayout { - Layout.fillWidth: true - Label { - id: statusLabel - Layout.fillWidth: true - color: palette.placeholderText - } - Button { - text: "Reload" - onClicked: { - editorArea.text = JSON.stringify(editorWindow.nmSettings.read(), null, 2); - statusLabel.text = "Reloaded"; - } - } - Button { - text: "Save" - onClicked: { - try { - const parsed = JSON.parse(editorArea.text); - nmSettings.write(parsed); - statusLabel.text = "Saved"; - } catch (e) { - statusLabel.text = "Parse error: " + e.message; - } - } - } - Button { - text: "Close" - onClicked: { - editorArea.focus = false; - editorWindow.destroy(); - } - } - } - } - } - } + ColumnLayout { + RowLayout { + Label { text: modelData.name; font.bold: true } + Label { text: modelData.address } + Label { text: `(Type: ${DeviceType.toString(modelData.type)})` } + } + RowLayout { + Label { + text: DeviceConnectionState.toString(modelData.state) + color: modelData.connected ? palette.link : palette.placeholderText + } + Label { + visible: Networking.backend == NetworkBackendType.NetworkManager && (modelData.state == DeviceConnectionState.Connecting || modelData.state == DeviceConnectionState.Disconnecting) + text: `(${NMDeviceState.toString(modelData.nmState)})` + } + Button { + visible: modelData.state == DeviceConnectionState.Connected + text: "Disconnect" + onClicked: modelData.disconnect() + } + CheckBox { + text: "Autoconnect" + checked: modelData.autoconnect + onClicked: modelData.autoconnect = !modelData.autoconnect + } + Label { + text: `Mode: ${WifiDeviceMode.toString(modelData.mode)}` + visible: modelData.type == DeviceType.Wifi + } + CheckBox { + text: "Scanner" + checked: modelData.scannerEnabled + onClicked: modelData.scannerEnabled = !modelData.scannerEnabled + visible: modelData.type === DeviceType.Wifi + } + } - FloatingWindow { - color: contentItem.palette.window + Repeater { + Layout.fillWidth: true + model: { + if (modelData.type !== DeviceType.Wifi) return [] + return [...modelData.networks.values].sort((a, b) => { + if (a.connected !== b.connected) { + return b.connected - a.connected + } + return b.signalStrength - a.signalStrength + }) + } - ColumnLayout { - anchors.fill: parent - anchors.margins: 5 + WrapperRectangle { + Layout.fillWidth: true + color: modelData.connected ? palette.highlight : palette.button + border.color: palette.mid + border.width: 1 + margin: 5 - ColumnLayout { - Label { - text: `Networking (${NetworkBackendType.toString(Networking.backend)} backend)` - font.bold: true - font.pointSize: 12 - } - RowLayout { - Label { - text: `Connectivity` - font.bold: true - } - Label { - text: `${NetworkConnectivity.toString(Networking.connectivity)}` - visible: Networking.canCheckConnectivity - } - Button { - text: "Re-check" - visible: Networking.canCheckConnectivity && Networking.connectivityCheckEnabled - onClicked: Networking.checkConnectivity() - } - CheckBox { - text: "Checking enabled" - checked: Networking.connectivityCheckEnabled - onClicked: Networking.connectivityCheckEnabled = !Networking.connectivityCheckEnabled - visible: Networking.canCheckConnectivity - } - CheckBox { - enabled: false - text: "Supported" - checked: Networking.canCheckConnectivity - } - } - } - - Column { - Layout.fillWidth: true - RowLayout { - Label { - text: "WiFi" - font.bold: true - } - CheckBox { - text: "Software" - checked: Networking.wifiEnabled - onClicked: Networking.wifiEnabled = !Networking.wifiEnabled - } - CheckBox { - enabled: false - text: "Hardware" - checked: Networking.wifiHardwareEnabled - } - } - } - - ListView { - clip: true - Layout.fillWidth: true - Layout.fillHeight: true - model: Networking.devices - - delegate: WrapperRectangle { - width: parent.width - color: "transparent" - border.color: palette.button - border.width: 1 - margin: 5 - - ColumnLayout { - RowLayout { - Label { - text: modelData.name - font.bold: true - } - Label { - text: modelData.address - } - Label { - text: `(Type: ${DeviceType.toString(modelData.type)})` - } - CheckBox { - text: `Managed` - checked: modelData.nmManaged - onClicked: modelData.nmManaged = !modelData.nmManaged - } - } - RowLayout { - Label { - text: ConnectionState.toString(modelData.state) - color: modelData.connected ? palette.link : palette.placeholderText - } - Button { - visible: modelData.state == ConnectionState.Connected - text: "Disconnect" - onClicked: modelData.disconnect() - } - CheckBox { - text: "Autoconnect" - checked: modelData.autoconnect - onClicked: modelData.autoconnect = !modelData.autoconnect - } - Label { - text: `Mode: ${WifiDeviceMode.toString(modelData.mode)}` - visible: modelData.type == DeviceType.Wifi - } - CheckBox { - text: "Scanner" - checked: modelData.scannerEnabled - onClicked: modelData.scannerEnabled = !modelData.scannerEnabled - visible: modelData.type === DeviceType.Wifi - } - } - - Repeater { - Layout.fillWidth: true - model: ScriptModel { - values: [...modelData.networks.values].sort((a, b) => { - if (a.connected !== b.connected) { - return b.connected - a.connected; - } - return b.signalStrength - a.signalStrength; - }) - } - - WrapperRectangle { - property var chosenSettings: { - const settings = modelData.nmSettings; - if (!settings || settings.length === 0) { - return null; - } - if (settings.length === 1) { - return settings[0]; - } - return settings[settingsComboBox.currentIndex]; - } - - Connections { - target: modelData - function onConnectionFailed(reason) { - failLoader.sourceComponent = failComponent; - failLoader.item.failReason = reason; - } - function onStateChanged() { - if (modelData.state == ConnectionState.Connecting) { - failLoader.sourceComponent = null; - } - } - } - - Component { - id: failComponent - RowLayout { - property var failReason - Label { - text: ConnectionFailReason.toString(failReason) - } - RowLayout { - TextField { - id: pskField - placeholderText: "PSK" - } - Button { - text: "Set" - visible: pskField.visible - onClicked: { - modelData.connectWithPsk(pskField.text); - failLoader.sourceComponent = null; - } - } - visible: modelData.security === WifiSecurityType.WpaPsk || modelData.security === WifiSecurityType.Wpa2Psk || modelData.security === WifiSecurityType.Sae - } - Button { - text: "Close" - onClicked: failLoader.sourceComponent = null - } - } - } - - Layout.fillWidth: true - color: modelData.connected ? palette.highlight : palette.button - border.color: palette.mid - border.width: 1 - margin: 5 - - RowLayout { - ColumnLayout { - Layout.fillWidth: true - RowLayout { - Label { - text: modelData.name - font.bold: true - } - Label { - text: modelData.known ? "Known" : "" - color: palette.placeholderText - } - } - RowLayout { - Label { - text: `Security: ${WifiSecurityType.toString(modelData.security)}` - color: palette.placeholderText - } - Label { - text: `| Signal strength: ${Math.round(modelData.signalStrength * 100)}%` - color: palette.placeholderText - } - } - } - ColumnLayout { - Layout.alignment: Qt.AlignRight - RowLayout { - Layout.alignment: Qt.AlignRight - BusyIndicator { - implicitHeight: 30 - implicitWidth: 30 - running: modelData.stateChanging - visible: modelData.stateChanging - } - Label { - text: ConnectionState.toString(modelData.state) - color: modelData.connected ? palette.link : palette.placeholderText - } - RowLayout { - Label { - text: "Choose settings:" - } - ComboBox { - id: settingsComboBox - model: modelData.nmSettings.map(s => s?.read()?.connection?.id) - currentIndex: 0 - } - visible: modelData.nmSettings.length > 1 - } - Button { - text: "Connect" - onClicked: { - if (chosenSettings) - modelData.connectWithSettings(chosenSettings); - else - modelData.connect(); - } - visible: !modelData.connected - } - Button { - text: "Disconnect" - onClicked: modelData.disconnect() - visible: modelData.connected - } - Button { - text: "Forget" - onClicked: modelData.forget() - visible: modelData.known - } - Button { - text: "Edit" - visible: modelData.known - onClicked: { - if (chosenSettings) - editorComponent.createObject(null, { - nmSettings: chosenSettings - }); - } - } - } - Loader { - id: failLoader - Layout.alignment: Qt.AlignRight - visible: sourceComponent !== null - } - } - } - } - } - } - } - } - } - } + RowLayout { + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Label { text: modelData.name; font.bold: true } + Label { + text: modelData.known ? "Known" : "" + color: palette.placeholderText + } + } + RowLayout { + Label { + text: `Security: ${WifiSecurityType.toString(modelData.security)}` + color: palette.placeholderText + } + Label { + text: `| Signal strength: ${Math.round(modelData.signalStrength*100)}%` + color: palette.placeholderText + } + } + Label { + visible: Networking.backend == NetworkBackendType.NetworkManager && (modelData.nmReason != NMConnectionStateReason.Unknown && modelData.nmReason != NMConnectionStateReason.None) + text: `Connection change reason: ${NMConnectionStateReason.toString(modelData.nmReason)}` + } + } + RowLayout { + Layout.alignment: Qt.AlignRight + Button { + text: "Connect" + onClicked: modelData.connect() + visible: !modelData.connected + } + Button { + text: "Disconnect" + onClicked: modelData.disconnect() + visible: modelData.connected + } + Button { + text: "Forget" + onClicked: modelData.forget() + visible: modelData.known + } + } + } + } + } + } + } + } + } } diff --git a/src/network/wifi.cpp b/src/network/wifi.cpp index e9939c2..57fb8ea 100644 --- a/src/network/wifi.cpp +++ b/src/network/wifi.cpp @@ -6,35 +6,99 @@ #include #include #include -#include #include "../core/logcat.hpp" #include "device.hpp" -#include "enums.hpp" #include "network.hpp" namespace qs::network { namespace { -QS_LOGGING_CATEGORY(logWifiNetwork, "quickshell.wifinetwork", QtWarningMsg); +QS_LOGGING_CATEGORY(logWifi, "quickshell.network.wifi", QtWarningMsg); +} // namespace + +QString WifiSecurityType::toString(WifiSecurityType::Enum type) { + switch (type) { + case Unknown: return QStringLiteral("Unknown"); + case Wpa3SuiteB192: return QStringLiteral("WPA3 Suite B 192-bit"); + case Sae: return QStringLiteral("WPA3"); + case Wpa2Eap: return QStringLiteral("WPA2 Enterprise"); + case Wpa2Psk: return QStringLiteral("WPA2"); + case WpaEap: return QStringLiteral("WPA Enterprise"); + case WpaPsk: return QStringLiteral("WPA"); + case StaticWep: return QStringLiteral("WEP"); + case DynamicWep: return QStringLiteral("Dynamic WEP"); + case Leap: return QStringLiteral("LEAP"); + case Owe: return QStringLiteral("OWE"); + case Open: return QStringLiteral("Open"); + default: return QStringLiteral("Unknown"); + } } +QString WifiDeviceMode::toString(WifiDeviceMode::Enum mode) { + switch (mode) { + case Unknown: return QStringLiteral("Unknown"); + case AdHoc: return QStringLiteral("Ad-Hoc"); + case Station: return QStringLiteral("Station"); + case AccessPoint: return QStringLiteral("Access Point"); + case Mesh: return QStringLiteral("Mesh"); + default: return QStringLiteral("Unknown"); + }; +} + +QString NMConnectionStateReason::toString(NMConnectionStateReason::Enum reason) { + switch (reason) { + case Unknown: return QStringLiteral("Unknown"); + case None: return QStringLiteral("No reason"); + case UserDisconnected: return QStringLiteral("User disconnection"); + case DeviceDisconnected: + return QStringLiteral("The device the connection was using was disconnected."); + case ServiceStopped: + return QStringLiteral("The service providing the VPN connection was stopped."); + case IpConfigInvalid: + return QStringLiteral("The IP config of the active connection was invalid."); + case ConnectTimeout: + return QStringLiteral("The connection attempt to the VPN service timed out."); + case ServiceStartTimeout: + return QStringLiteral( + "A timeout occurred while starting the service providing the VPN connection." + ); + case ServiceStartFailed: + return QStringLiteral("Starting the service providing the VPN connection failed."); + case NoSecrets: return QStringLiteral("Necessary secrets for the connection were not provided."); + case LoginFailed: return QStringLiteral("Authentication to the server failed."); + case ConnectionRemoved: + return QStringLiteral("Necessary secrets for the connection were not provided."); + case DependencyFailed: + return QStringLiteral("Master connection of this connection failed to activate."); + case DeviceRealizeFailed: return QStringLiteral("Could not create the software device link."); + case DeviceRemoved: return QStringLiteral("The device this connection depended on disappeared."); + default: return QStringLiteral("Unknown"); + }; +}; + WifiNetwork::WifiNetwork(QString ssid, QObject* parent): Network(std::move(ssid), parent) {}; -void WifiNetwork::connectWithPsk(const QString& psk) { +void WifiNetwork::connect() { if (this->bConnected) { - qCCritical(logWifiNetwork) << this << "is already connected."; + qCCritical(logWifi) << this << "is already connected."; return; } - if (this->bSecurity != WifiSecurityType::WpaPsk && this->bSecurity != WifiSecurityType::Wpa2Psk - && this->bSecurity != WifiSecurityType::Sae) - { - qCCritical(logWifiNetwork) << this << "has the wrong security type for a PSK."; - return; - } - emit this->requestConnectWithPsk(psk); + + this->requestConnect(); } +void WifiNetwork::disconnect() { + if (!this->bConnected) { + qCCritical(logWifi) << this << "is not currently connected"; + return; + } + + this->requestDisconnect(); +} + +void WifiNetwork::forget() { this->requestForget(); } + WifiDevice::WifiDevice(QObject* parent): NetworkDevice(DeviceType::Wifi, parent) {}; void WifiDevice::setScannerEnabled(bool enabled) { diff --git a/src/network/wifi.hpp b/src/network/wifi.hpp index c0091f7..15b093d 100644 --- a/src/network/wifi.hpp +++ b/src/network/wifi.hpp @@ -6,15 +6,90 @@ #include #include -#include "../core/doc.hpp" #include "../core/model.hpp" #include "device.hpp" -#include "enums.hpp" #include "network.hpp" namespace qs::network { -///! WiFi subtype of @@Network. +///! The security type of a wifi network. +class WifiSecurityType: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + Wpa3SuiteB192 = 0, + Sae = 1, + Wpa2Eap = 2, + Wpa2Psk = 3, + WpaEap = 4, + WpaPsk = 5, + StaticWep = 6, + DynamicWep = 7, + Leap = 8, + Owe = 9, + Open = 10, + Unknown = 11, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(WifiSecurityType::Enum type); +}; + +///! The 802.11 mode of a wifi device. +class WifiDeviceMode: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + /// The device is part of an Ad-Hoc network without a central access point. + AdHoc = 0, + /// The device is a station that can connect to networks. + Station = 1, + /// The device is a local hotspot/access point. + AccessPoint = 2, + /// The device is an 802.11s mesh point. + Mesh = 3, + /// The device mode is unknown. + Unknown = 4, + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(WifiDeviceMode::Enum mode); +}; + +///! NetworkManager-specific reason for a WifiNetworks connection state. +/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason. +class NMConnectionStateReason: public QObject { + Q_OBJECT; + QML_ELEMENT; + QML_SINGLETON; + +public: + enum Enum : quint8 { + Unknown = 0, + None = 1, + UserDisconnected = 2, + DeviceDisconnected = 3, + ServiceStopped = 4, + IpConfigInvalid = 5, + ConnectTimeout = 6, + ServiceStartTimeout = 7, + ServiceStartFailed = 8, + NoSecrets = 9, + LoginFailed = 10, + ConnectionRemoved = 11, + DependencyFailed = 12, + DeviceRealizeFailed = 13, + DeviceRemoved = 14 + }; + Q_ENUM(Enum); + Q_INVOKABLE static QString toString(NMConnectionStateReason::Enum reason); +}; + +///! An available wifi network. class WifiNetwork: public Network { Q_OBJECT; QML_ELEMENT; @@ -22,46 +97,58 @@ class WifiNetwork: public Network { // clang-format off /// The current signal strength of the network, from 0.0 to 1.0. Q_PROPERTY(qreal signalStrength READ default NOTIFY signalStrengthChanged BINDABLE bindableSignalStrength); + /// True if the wifi network has known connection settings saved. + Q_PROPERTY(bool known READ default NOTIFY knownChanged BINDABLE bindableKnown); /// The security type of the wifi network. Q_PROPERTY(WifiSecurityType::Enum security READ default NOTIFY securityChanged BINDABLE bindableSecurity); + /// A specific reason for the connection state when the backend is NetworkManager. + Q_PROPERTY(NMConnectionStateReason::Enum nmReason READ default NOTIFY nmReasonChanged BINDABLE bindableNmReason); // clang-format on public: explicit WifiNetwork(QString ssid, QObject* parent = nullptr); - /// Attempt to connect to the network with the given PSK. If the PSK is wrong, - /// a @@Network.connectionFailed(s) signal will be emitted with `NoSecrets`. + + /// Attempt to connect to the wifi network. /// - /// The networking backend may store the PSK for future use with @@Network.connect(). - /// As such, calling that function first is recommended to avoid having to show a - /// prompt if not required. - /// - /// > [!NOTE] PSKs should only be provided when the @@security is one of - /// > `WpaPsk`, `Wpa2Psk`, or `Sae`. - Q_INVOKABLE void connectWithPsk(const QString& psk); + /// > [!WARNING] Quickshell does not yet provide a NetworkManager authentication agent, + /// > meaning another agent will need to be active to enter passwords for unsaved networks. + Q_INVOKABLE void connect(); + /// Disconnect from the wifi network. + Q_INVOKABLE void disconnect(); + /// Forget all connection settings for this wifi network. + Q_INVOKABLE void forget(); QBindable bindableSignalStrength() { return &this->bSignalStrength; } + QBindable bindableKnown() { return &this->bKnown; } + QBindable bindableNmReason() { return &this->bNmReason; } QBindable bindableSecurity() { return &this->bSecurity; } signals: - QSDOC_HIDE void requestConnectWithPsk(QString psk); + void requestConnect(); + void requestDisconnect(); + void requestForget(); void signalStrengthChanged(); + void knownChanged(); void securityChanged(); + void nmReasonChanged(); private: // clang-format off Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, qreal, bSignalStrength, &WifiNetwork::signalStrengthChanged); + Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, bool, bKnown, &WifiNetwork::knownChanged); + Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, NMConnectionStateReason::Enum, bNmReason, &WifiNetwork::nmReasonChanged); Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, WifiSecurityType::Enum, bSecurity, &WifiNetwork::securityChanged); // clang-format on }; -///! WiFi variant of a @@NetworkDevice. +///! Wireless variant of a NetworkDevice. class WifiDevice: public NetworkDevice { Q_OBJECT; QML_ELEMENT; QML_UNCREATABLE(""); // clang-format off - /// A list of this available or connected wifi networks. + /// A list of this available and connected wifi networks. QSDOC_TYPE_OVERRIDE(ObjectModel*); Q_PROPERTY(UntypedObjectModel* networks READ networks CONSTANT); /// True when currently scanning for networks. @@ -77,9 +164,9 @@ public: void networkAdded(WifiNetwork* net); void networkRemoved(WifiNetwork* net); - [[nodiscard]] ObjectModel* networks() { return &this->mNetworks; } - QBindable bindableScannerEnabled() { return &this->bScannerEnabled; } - [[nodiscard]] bool scannerEnabled() const { return this->bScannerEnabled; } + [[nodiscard]] ObjectModel* networks() { return &this->mNetworks; }; + QBindable bindableScannerEnabled() { return &this->bScannerEnabled; }; + [[nodiscard]] bool scannerEnabled() const { return this->bScannerEnabled; }; void setScannerEnabled(bool enabled); QBindable bindableMode() { return &this->bMode; } diff --git a/src/wayland/buffer/manager.cpp b/src/wayland/buffer/manager.cpp index 9cf77fd..713752a 100644 --- a/src/wayland/buffer/manager.cpp +++ b/src/wayland/buffer/manager.cpp @@ -123,10 +123,6 @@ void WlBufferQSGDisplayNode::setRect(const QRectF& rect) { this->setMatrix(matrix); } -void WlBufferQSGDisplayNode::setFiltering(QSGTexture::Filtering filtering) { - this->imageNode->setFiltering(filtering); -} - void WlBufferQSGDisplayNode::syncSwapchain(const WlBufferSwapchain& swapchain) { auto* buffer = swapchain.frontbuffer(); auto& texture = swapchain.presentSecondBuffer ? this->buffer2 : this->buffer1; diff --git a/src/wayland/buffer/qsg.hpp b/src/wayland/buffer/qsg.hpp index bb05954..c230cfe 100644 --- a/src/wayland/buffer/qsg.hpp +++ b/src/wayland/buffer/qsg.hpp @@ -33,7 +33,6 @@ public: void syncSwapchain(const WlBufferSwapchain& swapchain); void setRect(const QRectF& rect); - void setFiltering(QSGTexture::Filtering filtering); private: QQuickWindow* window; diff --git a/src/wayland/screencopy/view.cpp b/src/wayland/screencopy/view.cpp index 7d10dc2..7828c98 100644 --- a/src/wayland/screencopy/view.cpp +++ b/src/wayland/screencopy/view.cpp @@ -167,7 +167,6 @@ QSGNode* ScreencopyView::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData* auto& swapchain = this->context->swapchain(); node->syncSwapchain(swapchain); node->setRect(this->boundingRect()); - node->setFiltering(QSGTexture::Linear); // NOLINT (misc-include-cleaner) if (this->mLive) this->context->captureFrame(); return node;