diff --git a/CMakeLists.txt b/CMakeLists.txt index 81e896f..257ad94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,6 @@ boption(SERVICE_GREETD "Greetd" ON) boption(SERVICE_UPOWER "UPower" ON) boption(SERVICE_NOTIFICATIONS "Notifications" ON) boption(BLUETOOTH "Bluetooth" ON) -boption(NETWORK "Network" ON) include(cmake/install-qml-module.cmake) include(cmake/util.cmake) @@ -126,7 +125,7 @@ if (WAYLAND) list(APPEND QT_PRIVDEPS WaylandClientPrivate) endif() -if (SERVICE_STATUS_NOTIFIER OR SERVICE_MPRIS OR SERVICE_UPOWER OR SERVICE_NOTIFICATIONS OR BLUETOOTH OR NETWORK) +if (SERVICE_STATUS_NOTIFIER OR SERVICE_MPRIS OR SERVICE_UPOWER OR SERVICE_NOTIFICATIONS OR BLUETOOTH) set(DBUS ON) endif() diff --git a/changelog/next.md b/changelog/next.md index 0cdff57..3a932ed 100644 --- a/changelog/next.md +++ b/changelog/next.md @@ -20,8 +20,6 @@ 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 initial support for network management. -- Added support for grabbing focus from popup windows. ## Other Changes diff --git a/default.nix b/default.nix index 0b6f303..4561cc6 100644 --- a/default.nix +++ b/default.nix @@ -46,7 +46,6 @@ withHyprland ? true, withI3 ? true, withPolkit ? true, - withNetworkManager ? true, }: let unwrapped = stdenv.mkDerivation { pname = "quickshell${lib.optionalString debug "-debug"}"; @@ -96,7 +95,6 @@ (lib.cmakeBool "SCREENCOPY" (libgbm != null)) (lib.cmakeBool "SERVICE_PIPEWIRE" withPipewire) (lib.cmakeBool "SERVICE_PAM" withPam) - (lib.cmakeBool "SERVICE_NETWORKMANAGER" withNetworkManager) (lib.cmakeBool "SERVICE_POLKIT" withPolkit) (lib.cmakeBool "HYPRLAND" withHyprland) (lib.cmakeBool "I3" withI3) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c95ecf7..52db00a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,3 @@ add_subdirectory(services) if (BLUETOOTH) add_subdirectory(bluetooth) endif() - -if (NETWORK) - add_subdirectory(network) -endif() diff --git a/src/core/popupanchor.cpp b/src/core/popupanchor.cpp index 151dd5d..bbcc3a5 100644 --- a/src/core/popupanchor.cpp +++ b/src/core/popupanchor.cpp @@ -28,7 +28,7 @@ void PopupAnchor::markClean() { this->lastState = this->state; } void PopupAnchor::markDirty() { this->lastState.reset(); } QWindow* PopupAnchor::backingWindow() const { - return this->bProxyWindow ? this->bProxyWindow->backingWindow() : nullptr; + return this->mProxyWindow ? this->mProxyWindow->backingWindow() : nullptr; } void PopupAnchor::setWindowInternal(QObject* window) { @@ -36,14 +36,14 @@ void PopupAnchor::setWindowInternal(QObject* window) { if (this->mWindow) { QObject::disconnect(this->mWindow, nullptr, this, nullptr); - QObject::disconnect(this->bProxyWindow, nullptr, this, nullptr); + QObject::disconnect(this->mProxyWindow, nullptr, this, nullptr); } if (window) { if (auto* proxy = qobject_cast(window)) { - this->bProxyWindow = proxy; + this->mProxyWindow = proxy; } else if (auto* interface = qobject_cast(window)) { - this->bProxyWindow = interface->proxyWindow(); + this->mProxyWindow = interface->proxyWindow(); } else { qWarning() << "Tried to set popup anchor window to" << window << "which is not a quickshell window."; @@ -55,7 +55,7 @@ void PopupAnchor::setWindowInternal(QObject* window) { QObject::connect(this->mWindow, &QObject::destroyed, this, &PopupAnchor::onWindowDestroyed); QObject::connect( - this->bProxyWindow, + this->mProxyWindow, &ProxyWindowBase::backerVisibilityChanged, this, &PopupAnchor::backingWindowVisibilityChanged @@ -70,7 +70,7 @@ void PopupAnchor::setWindowInternal(QObject* window) { setnull: if (this->mWindow) { this->mWindow = nullptr; - this->bProxyWindow = nullptr; + this->mProxyWindow = nullptr; emit this->windowChanged(); emit this->backingWindowVisibilityChanged(); @@ -100,7 +100,7 @@ void PopupAnchor::setItem(QQuickItem* item) { void PopupAnchor::onWindowDestroyed() { this->mWindow = nullptr; - this->bProxyWindow = nullptr; + this->mProxyWindow = nullptr; emit this->windowChanged(); emit this->backingWindowVisibilityChanged(); } @@ -186,11 +186,11 @@ void PopupAnchor::updatePlacement(const QPoint& anchorpoint, const QSize& size) } void PopupAnchor::updateAnchor() { - if (this->mItem && this->bProxyWindow) { + if (this->mItem && this->mProxyWindow) { auto baseRect = this->mUserRect.isEmpty() ? this->mItem->boundingRect() : this->mUserRect.qrect(); - auto rect = this->bProxyWindow->contentItem()->mapFromItem( + auto rect = this->mProxyWindow->contentItem()->mapFromItem( this->mItem, baseRect.marginsRemoved(this->mMargins.qmargins()) ); diff --git a/src/core/popupanchor.hpp b/src/core/popupanchor.hpp index 9f08512..a9b121e 100644 --- a/src/core/popupanchor.hpp +++ b/src/core/popupanchor.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -140,9 +139,7 @@ public: void markDirty(); [[nodiscard]] QObject* window() const { return this->mWindow; } - [[nodiscard]] QBindable bindableProxyWindow() const { - return &this->bProxyWindow; - } + [[nodiscard]] ProxyWindowBase* proxyWindow() const { return this->mProxyWindow; } [[nodiscard]] QWindow* backingWindow() const; void setWindowInternal(QObject* window); void setWindow(QObject* window); @@ -196,12 +193,11 @@ private slots: private: QObject* mWindow = nullptr; QQuickItem* mItem = nullptr; + ProxyWindowBase* mProxyWindow = nullptr; PopupAnchorState state; Box mUserRect; Margins mMargins; std::optional lastState; - - Q_OBJECT_BINDABLE_PROPERTY(PopupAnchor, ProxyWindowBase*, bProxyWindow); }; class PopupPositioner { diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt deleted file mode 100644 index 6075040..0000000 --- a/src/network/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -add_subdirectory(nm) - -qt_add_library(quickshell-network STATIC - network.cpp - device.cpp - wifi.cpp -) - -target_include_directories(quickshell-network PRIVATE - ${CMAKE_CURRENT_BINARY_DIR} -) - -qt_add_qml_module(quickshell-network - URI Quickshell.Networking - VERSION 0.1 - DEPENDENCIES QtQml -) - -qs_add_module_deps_light(quickshell-network Quickshell) -install_qml_module(quickshell-network) -target_link_libraries(quickshell-network PRIVATE quickshell-network-nm Qt::Qml Qt::DBus) -qs_add_link_dependencies(quickshell-network quickshell-dbus) -target_link_libraries(quickshell PRIVATE quickshell-networkplugin) -qs_module_pch(quickshell-network SET dbus) diff --git a/src/network/device.cpp b/src/network/device.cpp deleted file mode 100644 index a47a5ee..0000000 --- a/src/network/device.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "device.hpp" - -#include -#include -#include -#include -#include -#include - -#include "../core/logcat.hpp" - -namespace qs::network { - -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 == DeviceConnectionState::Connected; - }); -}; - -void NetworkDevice::setAutoconnect(bool autoconnect) { - if (this->bAutoconnect == autoconnect) return; - emit this->requestSetAutoconnect(autoconnect); -} - -void NetworkDevice::disconnect() { - if (this->bState == DeviceConnectionState::Disconnected) { - qCCritical(logNetworkDevice) << "Device" << this << "is already disconnected"; - return; - } - if (this->bState == DeviceConnectionState::Disconnecting) { - qCCritical(logNetworkDevice) << "Device" << this << "is already disconnecting"; - return; - } - qCDebug(logNetworkDevice) << "Disconnecting from device" << this; - this->requestDisconnect(); -} - -} // namespace qs::network diff --git a/src/network/device.hpp b/src/network/device.hpp deleted file mode 100644 index f3807c2..0000000 --- a/src/network/device.hpp +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -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. -/// 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. - 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); - /// The hardware address of the device in the XX:XX:XX:XX:XX:XX format. - Q_PROPERTY(QString address READ default NOTIFY addressChanged BINDABLE bindableAddress); - /// 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::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 - -public: - explicit NetworkDevice(DeviceType::Enum type, QObject* parent = nullptr); - - /// 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 bindableNmState() { return &this->bNmState; }; - [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; }; - QBindable bindableAutoconnect() { return &this->bAutoconnect; }; - void setAutoconnect(bool autoconnect); - -signals: - void requestDisconnect(); - void requestSetAutoconnect(bool autoconnect); - void nameChanged(); - void addressChanged(); - void connectedChanged(); - void stateChanged(); - void nmStateChanged(); - void autoconnectChanged(); - -private: - DeviceType::Enum mType; - // clang-format off - 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, 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 -}; - -} // namespace qs::network diff --git a/src/network/module.md b/src/network/module.md deleted file mode 100644 index a0c8e64..0000000 --- a/src/network/module.md +++ /dev/null @@ -1,13 +0,0 @@ -name = "Quickshell.Networking" -description = "Network API" -headers = [ - "network.hpp", - "device.hpp", - "wifi.hpp", -] ------ -This module exposes Network management APIs provided by a supported network backend. -For now, the only backend available is the NetworkManager DBus interface. -Both DBus and NetworkManager must be running to use it. - -See the @@Quickshell.Networking.Networking singleton. diff --git a/src/network/network.cpp b/src/network/network.cpp deleted file mode 100644 index 67ed6a5..0000000 --- a/src/network/network.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "network.hpp" -#include - -#include -#include -#include -#include -#include - -#include "../core/logcat.hpp" -#include "device.hpp" -#include "nm/backend.hpp" - -namespace qs::network { - -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()) { - QObject::connect(nm, &NetworkManager::deviceAdded, this, &Networking::deviceAdded); - QObject::connect(nm, &NetworkManager::deviceRemoved, this, &Networking::deviceRemoved); - QObject::connect(this, &Networking::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled); - this->bindableWifiEnabled().setBinding([nm]() { return nm->wifiEnabled(); }); - this->bindableWifiHardwareEnabled().setBinding([nm]() { return nm->wifiHardwareEnabled(); }); - - this->mBackend = nm; - this->mBackendType = NetworkBackendType::NetworkManager; - return; - } else { - delete nm; - } - - 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::setWifiEnabled(bool enabled) { - if (this->bWifiEnabled == enabled) return; - emit this->requestSetWifiEnabled(enabled); -} - -Network::Network(QString name, QObject* parent): QObject(parent), mName(std::move(name)) { - this->bStateChanging.setBinding([this] { - auto state = this->bState.value(); - return state == NetworkState::Connecting || state == NetworkState::Disconnecting; - }); -}; - -} // namespace qs::network diff --git a/src/network/network.hpp b/src/network/network.hpp deleted file mode 100644 index 8af7c9d..0000000 --- a/src/network/network.hpp +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "../core/model.hpp" -#include "device.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; - -public: - [[nodiscard]] virtual bool isAvailable() const = 0; - -protected: - explicit NetworkBackend(QObject* parent = nullptr): QObject(parent) {}; -}; - -///! 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 Networking: public QObject { - Q_OBJECT; - QML_SINGLETON; - QML_ELEMENT; - // clang-format off - /// 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. - Q_PROPERTY(qs::network::NetworkBackendType::Enum backend READ backend CONSTANT); - /// Switch for the rfkill software block of all wireless devices. - 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); - // clang-format on - -public: - explicit Networking(QObject* parent = nullptr); - - [[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(); - -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. -class Network: public QObject { - Q_OBJECT; - QML_ELEMENT; - 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); - /// True if the network is connected. - Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected); - /// The connectivity state of the network. - 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); - - [[nodiscard]] QString name() const { return this->mName; }; - QBindable bindableConnected() { return &this->bConnected; } - QBindable bindableState() { return &this->bState; } - QBindable bindableStateChanging() { return &this->bStateChanging; } - -signals: - void connectedChanged(); - void stateChanged(); - void stateChangingChanged(); - -protected: - QString mName; - - Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bConnected, &Network::connectedChanged); - Q_OBJECT_BINDABLE_PROPERTY(Network, NetworkState::Enum, bState, &Network::stateChanged); - Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bStateChanging, &Network::stateChangingChanged); -}; - -} // namespace qs::network diff --git a/src/network/nm/CMakeLists.txt b/src/network/nm/CMakeLists.txt deleted file mode 100644 index bb8635e..0000000 --- a/src/network/nm/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -set_source_files_properties(org.freedesktop.NetworkManager.xml PROPERTIES - CLASSNAME DBusNetworkManagerProxy - NO_NAMESPACE TRUE - INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/dbus_types.hpp -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.xml - dbus_nm_backend -) - -set_source_files_properties(org.freedesktop.NetworkManager.Device.xml PROPERTIES - CLASSNAME DBusNMDeviceProxy - NO_NAMESPACE TRUE -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.Device.xml - dbus_nm_device -) - -set_source_files_properties(org.freedesktop.NetworkManager.Device.Wireless.xml PROPERTIES - CLASSNAME DBusNMWirelessProxy - NO_NAMESPACE TRUE -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.Device.Wireless.xml - dbus_nm_wireless -) - -set_source_files_properties(org.freedesktop.NetworkManager.AccessPoint.xml PROPERTIES - CLASSNAME DBusNMAccessPointProxy - NO_NAMESPACE TRUE -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.AccessPoint.xml - dbus_nm_accesspoint -) - -set_source_files_properties(org.freedesktop.NetworkManager.Settings.Connection.xml PROPERTIES - CLASSNAME DBusNMConnectionSettingsProxy - NO_NAMESPACE TRUE - INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/dbus_types.hpp -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.Settings.Connection.xml - dbus_nm_connection_settings -) - -set_source_files_properties(org.freedesktop.NetworkManager.Connection.Active.xml PROPERTIES - CLASSNAME DBusNMActiveConnectionProxy - NO_NAMESPACE TRUE -) - -qt_add_dbus_interface(NM_DBUS_INTERFACES - org.freedesktop.NetworkManager.Connection.Active.xml - dbus_nm_active_connection -) - -qt_add_library(quickshell-network-nm STATIC - backend.cpp - device.cpp - connection.cpp - accesspoint.cpp - wireless.cpp - utils.cpp - enums.hpp - ${NM_DBUS_INTERFACES} -) - -target_include_directories(quickshell-network-nm PUBLIC - ${CMAKE_CURRENT_BINARY_DIR} -) - -target_link_libraries(quickshell-network-nm PRIVATE Qt::Qml Qt::DBus) -qs_add_link_dependencies(quickshell-network-nm quickshell-dbus) diff --git a/src/network/nm/accesspoint.cpp b/src/network/nm/accesspoint.cpp deleted file mode 100644 index b6e3dfb..0000000 --- a/src/network/nm/accesspoint.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "accesspoint.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "dbus_nm_accesspoint.h" -#include "enums.hpp" - -namespace qs::network { -using namespace qs::dbus; - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); -} - -NMAccessPoint::NMAccessPoint(const QString& path, QObject* parent): QObject(parent) { - this->proxy = new DBusNMAccessPointProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - if (!this->proxy->isValid()) { - qCWarning(logNetworkManager) << "Cannot create DBus interface for access point at" << path; - return; - } - - QObject::connect( - &this->accessPointProperties, - &DBusPropertyGroup::getAllFinished, - this, - &NMAccessPoint::loaded, - Qt::SingleShotConnection - ); - - this->accessPointProperties.setInterface(this->proxy); - this->accessPointProperties.updateAllViaGetAll(); -} - -bool NMAccessPoint::isValid() const { return this->proxy && this->proxy->isValid(); } -QString NMAccessPoint::address() const { return this->proxy ? this->proxy->service() : QString(); } -QString NMAccessPoint::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)); -} - -DBusResult -DBusDataTransform::fromWire(quint32 wire) { - return DBusResult(static_cast(wire)); -} - -DBusResult -DBusDataTransform::fromWire(quint32 wire) { - return DBusResult(static_cast(wire)); -} - -} // namespace qs::dbus diff --git a/src/network/nm/accesspoint.hpp b/src/network/nm/accesspoint.hpp deleted file mode 100644 index 8409089..0000000 --- a/src/network/nm/accesspoint.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "../../dbus/properties.hpp" -#include "../wifi.hpp" -#include "dbus_nm_accesspoint.h" -#include "enums.hpp" - -namespace qs::dbus { - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NM80211ApFlags::Enum; - static DBusResult fromWire(Wire wire); -}; - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NM80211ApSecurityFlags::Enum; - static DBusResult fromWire(Wire wire); -}; - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NM80211Mode::Enum; - static DBusResult fromWire(Wire wire); -}; - -} // namespace qs::dbus - -namespace qs::network { - -/// Proxy of a /org/freedesktop/NetworkManager/AccessPoint/* object. -class NMAccessPoint: public QObject { - Q_OBJECT; - -public: - explicit NMAccessPoint(const QString& path, QObject* parent = nullptr); - - [[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; }; - -signals: - void loaded(); - void ssidChanged(const QByteArray& ssid); - void signalStrengthChanged(quint8 signal); - void flagsChanged(NM80211ApFlags::Enum flags); - void wpaFlagsChanged(NM80211ApSecurityFlags::Enum wpaFlags); - void rsnFlagsChanged(NM80211ApSecurityFlags::Enum rsnFlags); - void modeChanged(NM80211Mode::Enum mode); - void securityChanged(WifiSecurityType::Enum security); - -private: - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, QByteArray, bSsid, &NMAccessPoint::ssidChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, quint8, bSignalStrength, &NMAccessPoint::signalStrengthChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApFlags::Enum, bFlags, &NMAccessPoint::flagsChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApSecurityFlags::Enum, bWpaFlags, &NMAccessPoint::wpaFlagsChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApSecurityFlags::Enum, bRsnFlags, &NMAccessPoint::rsnFlagsChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211Mode::Enum, bMode, &NMAccessPoint::modeChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, WifiSecurityType::Enum, bSecurity, &NMAccessPoint::securityChanged); - - QS_DBUS_BINDABLE_PROPERTY_GROUP(NMAccessPointAdapter, accessPointProperties); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pSsid, bSsid, accessPointProperties, "Ssid"); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pSignalStrength, bSignalStrength, accessPointProperties, "Strength"); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pFlags, bFlags, accessPointProperties, "Flags"); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pWpaFlags, bWpaFlags, accessPointProperties, "WpaFlags"); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pRsnFlags, bRsnFlags, accessPointProperties, "RsnFlags"); - QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pMode, bMode, accessPointProperties, "Mode"); - // clang-format on - - DBusNMAccessPointProxy* proxy = nullptr; -}; - -} // namespace qs::network diff --git a/src/network/nm/backend.cpp b/src/network/nm/backend.cpp deleted file mode 100644 index 4b61e33..0000000 --- a/src/network/nm/backend.cpp +++ /dev/null @@ -1,270 +0,0 @@ -#include "backend.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "../device.hpp" -#include "../network.hpp" -#include "../wifi.hpp" -#include "dbus_nm_backend.h" -#include "dbus_nm_device.h" -#include "dbus_types.hpp" -#include "device.hpp" -#include "enums.hpp" -#include "wireless.hpp" - -namespace qs::network { - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); -} - -NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) { - qDBusRegisterMetaType(); - - auto bus = QDBusConnection::systemBus(); - if (!bus.isConnected()) { - qCWarning( - logNetworkManager - ) << "Could not connect to DBus. NetworkManager backend will not work."; - return; - } - - this->proxy = new DBusNetworkManagerProxy( - "org.freedesktop.NetworkManager", - "/org/freedesktop/NetworkManager", - bus, - this - ); - - if (!this->proxy->isValid()) { - qCDebug( - logNetworkManager - ) << "NetworkManager is not currently running. This network backend will not work"; - } else { - this->init(); - } -} - -void NetworkManager::init() { - // clang-format off - QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceAdded, this, &NetworkManager::onDevicePathAdded); - QObject::connect(this->proxy, &DBusNetworkManagerProxy::DeviceRemoved, this, &NetworkManager::onDevicePathRemoved); - // clang-format on - - this->dbusProperties.setInterface(this->proxy); - this->dbusProperties.updateAllViaGetAll(); - - this->registerDevices(); -} - -void NetworkManager::registerDevices() { - auto pending = this->proxy->GetAllDevices(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply> reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) << "Failed to get devices: " << reply.error().message(); - } else { - for (const QDBusObjectPath& devicePath: reply.value()) { - this->registerDevice(devicePath.path()); - } - } - - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -void NetworkManager::registerDevice(const QString& path) { - if (this->mDevices.contains(path)) { - qCDebug(logNetworkManager) << "Skipping duplicate registration of device" << path; - return; - } - - auto* temp = new DBusNMDeviceProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - auto callback = [this, path, temp](uint value, const QDBusError& error) { - if (error.isValid()) { - qCWarning(logNetworkManager) << "Failed to get device type:" << error; - } else { - auto type = static_cast(value); - NMDevice* dev = nullptr; - this->mDevices.insert(path, nullptr); - - switch (type) { - case NMDeviceType::Wifi: dev = new NMWirelessDevice(path); break; - default: break; - } - - if (dev) { - 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 - - if (dev->managed()) this->registerFrontendDevice(type, dev); - } - } - temp->deleteLater(); - } - }; - - qs::dbus::asyncReadProperty(*temp, "DeviceType", callback); -} - -void NetworkManager::registerFrontendDevice(NMDeviceType::Enum type, NMDevice* dev) { - NetworkDevice* frontendDev = nullptr; - switch (type) { - case NMDeviceType::Wifi: { - auto* frontendWifiDev = new WifiDevice(dev); - auto* wifiDev = qobject_cast(dev); - // Bind WifiDevice-specific properties - auto translateMode = [wifiDev]() { - switch (wifiDev->mode()) { - case NM80211Mode::Unknown: return WifiDeviceMode::Unknown; - case NM80211Mode::Adhoc: return WifiDeviceMode::AdHoc; - case NM80211Mode::Infra: return WifiDeviceMode::Station; - case NM80211Mode::Ap: return WifiDeviceMode::AccessPoint; - case NM80211Mode::Mesh: return WifiDeviceMode::Mesh; - } - }; - // clang-format off - frontendWifiDev->bindableMode().setBinding(translateMode); - wifiDev->bindableScanning().setBinding([frontendWifiDev]() { return frontendWifiDev->scannerEnabled(); }); - QObject::connect(wifiDev, &NMWirelessDevice::networkAdded, frontendWifiDev, &WifiDevice::networkAdded); - QObject::connect(wifiDev, &NMWirelessDevice::networkRemoved, frontendWifiDev, &WifiDevice::networkRemoved); - // clang-format on - frontendDev = frontendWifiDev; - break; - } - default: return; - } - - // Bind generic NetworkDevice properties - auto translateState = [dev]() { - switch (dev->state()) { - 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(); }); - QObject::connect(frontendDev, &WifiDevice::requestDisconnect, dev, &NMDevice::disconnect); - QObject::connect(frontendDev, &NetworkDevice::requestSetAutoconnect, dev, &NMDevice::setAutoconnect); - // clang-format on - - this->mFrontendDevices.insert(dev->path(), frontendDev); - emit this->deviceAdded(frontendDev); -} - -void NetworkManager::removeFrontendDevice(NMDevice* dev) { - auto* frontendDev = this->mFrontendDevices.take(dev->path()); - if (frontendDev) { - emit this->deviceRemoved(frontendDev); - frontendDev->deleteLater(); - } -} - -void NetworkManager::onDevicePathAdded(const QDBusObjectPath& path) { - this->registerDevice(path.path()); -} - -void NetworkManager::onDevicePathRemoved(const QDBusObjectPath& path) { - auto iter = this->mDevices.find(path.path()); - if (iter == this->mDevices.end()) { - qCWarning(logNetworkManager) << "Sent removal signal for" << path.path() - << "which is not registered."; - } else { - auto* dev = iter.value(); - this->mDevices.erase(iter); - if (dev) { - this->removeFrontendDevice(dev); - delete dev; - } - } -} - -void NetworkManager::activateConnection( - const QDBusObjectPath& connPath, - const QDBusObjectPath& devPath -) { - auto pending = this->proxy->ActivateConnection(connPath, devPath, QDBusObjectPath("/")); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [](QDBusPendingCallWatcher* call) { - const QDBusPendingReply reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) << "Failed to activate connection:" << reply.error().message(); - } - delete call; - }; - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -void NetworkManager::addAndActivateConnection( - const ConnectionSettingsMap& settings, - const QDBusObjectPath& devPath, - const QDBusObjectPath& specificObjectPath -) { - auto pending = this->proxy->AddAndActivateConnection(settings, devPath, specificObjectPath); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [](QDBusPendingCallWatcher* call) { - const QDBusPendingReply reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to add and activate connection:" << reply.error().message(); - } - delete call; - }; - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -void NetworkManager::setWifiEnabled(bool enabled) { - if (enabled == this->bWifiEnabled) return; - this->bWifiEnabled = enabled; - this->pWifiEnabled.write(); -} - -bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); }; - -} // namespace qs::network diff --git a/src/network/nm/backend.hpp b/src/network/nm/backend.hpp deleted file mode 100644 index 471f57a..0000000 --- a/src/network/nm/backend.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "../../dbus/properties.hpp" -#include "../network.hpp" -#include "dbus_nm_backend.h" -#include "device.hpp" - -namespace qs::network { - -class NetworkManager: public NetworkBackend { - Q_OBJECT; - -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; }; - -signals: - void deviceAdded(NetworkDevice* device); - void deviceRemoved(NetworkDevice* device); - void wifiEnabledChanged(bool enabled); - void wifiHardwareEnabledChanged(bool enabled); - -public slots: - void setWifiEnabled(bool enabled); - -private slots: - void onDevicePathAdded(const QDBusObjectPath& path); - void onDevicePathRemoved(const QDBusObjectPath& path); - void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath); - void addAndActivateConnection( - const ConnectionSettingsMap& settings, - const QDBusObjectPath& devPath, - const QDBusObjectPath& specificObjectPath - ); - -private: - void init(); - void registerDevices(); - void registerDevice(const QString& path); - void registerFrontendDevice(NMDeviceType::Enum type, NMDevice* dev); - void removeFrontendDevice(NMDevice* dev); - - QHash mDevices; - QHash mFrontendDevices; - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiEnabled, &NetworkManager::wifiEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiHardwareEnabled, &NetworkManager::wifiHardwareEnabledChanged); - - 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"); - // clang-format on - DBusNetworkManagerProxy* proxy = nullptr; -}; - -} // namespace qs::network diff --git a/src/network/nm/connection.cpp b/src/network/nm/connection.cpp deleted file mode 100644 index 39b6f66..0000000 --- a/src/network/nm/connection.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#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/connection.hpp b/src/network/nm/connection.hpp deleted file mode 100644 index 4f126c8..0000000 --- a/src/network/nm/connection.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#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 { - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NMConnectionState::Enum; - static DBusResult fromWire(Wire wire); -}; - -} // namespace qs::dbus - -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; - -public: - explicit NMActiveConnection(const QString& path, QObject* parent = nullptr); - - [[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->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); - - 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; -}; - -} // namespace qs::network diff --git a/src/network/nm/dbus_types.hpp b/src/network/nm/dbus_types.hpp deleted file mode 100644 index dadbcf3..0000000 --- a/src/network/nm/dbus_types.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -using ConnectionSettingsMap = QMap; -Q_DECLARE_METATYPE(ConnectionSettingsMap); diff --git a/src/network/nm/device.cpp b/src/network/nm/device.cpp deleted file mode 100644 index aad565d..0000000 --- a/src/network/nm/device.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "device.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "../device.hpp" -#include "connection.hpp" -#include "dbus_nm_device.h" - -namespace qs::network { -using namespace qs::dbus; - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); -} - -NMDevice::NMDevice(const QString& path, QObject* parent): QObject(parent) { - this->deviceProxy = new DBusNMDeviceProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - if (!this->deviceProxy->isValid()) { - qCWarning(logNetworkManager) << "Cannot create DBus interface for device at" << path; - return; - } - - // clang-format off - QObject::connect(this, &NMDevice::availableConnectionPathsChanged, this, &NMDevice::onAvailableConnectionPathsChanged); - QObject::connect(this, &NMDevice::activeConnectionPathChanged, this, &NMDevice::onActiveConnectionPathChanged); - // clang-format on - - this->deviceProperties.setInterface(this->deviceProxy); - this->deviceProperties.updateAllViaGetAll(); -} - -void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) { - const QString stringPath = path.path(); - - // Remove old active connection - if (this->mActiveConnection) { - QObject::disconnect(this->mActiveConnection, nullptr, this, nullptr); - delete this->mActiveConnection; - this->mActiveConnection = nullptr; - } - - // Create new active connection - if (stringPath != "/") { - auto* active = new NMActiveConnection(stringPath, this); - if (!active->isValid()) { - qCWarning(logNetworkManager) << "Ignoring invalid registration of" << stringPath; - delete active; - } else { - this->mActiveConnection = active; - QObject::connect( - active, - &NMActiveConnection::loaded, - this, - [this, active]() { emit this->activeConnectionLoaded(active); }, - Qt::SingleShotConnection - ); - } - } -} - -void NMDevice::onAvailableConnectionPathsChanged(const QList& paths) { - QSet newPathSet; - for (const QDBusObjectPath& path: paths) { - newPathSet.insert(path.path()); - } - const auto existingPaths = this->mConnections.keys(); - const QSet existingPathSet(existingPaths.begin(), existingPaths.end()); - - const auto addedConnections = newPathSet - existingPathSet; - const auto removedConnections = existingPathSet - newPathSet; - - for (const QString& path: addedConnections) { - this->registerConnection(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 { - delete connection; - } - }; -} - -void NMDevice::registerConnection(const QString& path) { - auto* connection = new NMConnectionSettings(path, this); - if (!connection->isValid()) { - qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; - delete connection; - } else { - this->mConnections.insert(path, connection); - QObject::connect( - connection, - &NMConnectionSettings::loaded, - this, - [this, connection]() { emit this->connectionLoaded(connection); }, - Qt::SingleShotConnection - ); - } -} - -void NMDevice::disconnect() { this->deviceProxy->Disconnect(); } - -void NMDevice::setAutoconnect(bool autoconnect) { - if (autoconnect == this->bAutoconnect) return; - this->bAutoconnect = autoconnect; - this->pAutoconnect.write(); -} - -bool NMDevice::isValid() const { return this->deviceProxy && this->deviceProxy->isValid(); } -QString NMDevice::address() const { - return this->deviceProxy ? this->deviceProxy->service() : QString(); -} -QString NMDevice::path() const { return this->deviceProxy ? this->deviceProxy->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/device.hpp b/src/network/nm/device.hpp deleted file mode 100644 index e3ff4b9..0000000 --- a/src/network/nm/device.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "../../dbus/properties.hpp" -#include "connection.hpp" -#include "dbus_nm_device.h" - -namespace qs::dbus { - -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NMDeviceState::Enum; - static DBusResult fromWire(Wire wire); -}; - -} // namespace qs::dbus - -namespace qs::network { - -// Proxy of a /org/freedesktop/NetworkManager/Device/* object. -// Only the members from the org.freedesktop.NetworkManager.Device interface. -// Owns the lifetime of NMActiveConnection(s) and NMConnectionSetting(s). -class NMDevice: public QObject { - Q_OBJECT; - -public: - explicit NMDevice(const QString& path, QObject* parent = nullptr); - - [[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]] 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 ConnectionSettingsMap& settings, - const QDBusObjectPath& devPath, - const QDBusObjectPath& apPath - ); - 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 autoconnectChanged(bool autoconnect); - -public slots: - void disconnect(); - void setAutoconnect(bool autoconnect); - -private slots: - void onAvailableConnectionPathsChanged(const QList& paths); - void onActiveConnectionPathChanged(const QDBusObjectPath& path); - -private: - void registerConnection(const QString& path); - - QHash mConnections; - NMActiveConnection* mActiveConnection = nullptr; - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QString, bInterface, &NMDevice::interfaceChanged); - 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, bool, bAutoconnect, &NMDevice::autoconnectChanged); - 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); - QS_DBUS_PROPERTY_BINDING(NMDevice, pName, bInterface, deviceProperties, "Interface"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pAddress, bHwAddress, deviceProperties, "HwAddress"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pManaged, bManaged, deviceProperties, "Managed"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pState, bState, deviceProperties, "State"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pAutoconnect, bAutoconnect, deviceProperties, "Autoconnect"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pAvailableConnections, bAvailableConnections, deviceProperties, "AvailableConnections"); - QS_DBUS_PROPERTY_BINDING(NMDevice, pActiveConnection, bActiveConnection, deviceProperties, "ActiveConnection"); - // clang-format on - - DBusNMDeviceProxy* deviceProxy = nullptr; -}; - -} // namespace qs::network diff --git a/src/network/nm/enums.hpp b/src/network/nm/enums.hpp deleted file mode 100644 index 34e5b65..0000000 --- a/src/network/nm/enums.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace qs::network { - -// Indicates the type of hardware represented by a device object. -class NMDeviceType: public QObject { - Q_OBJECT; - -public: - enum Enum : quint8 { - Unknown = 0, - Ethernet = 1, - Wifi = 2, - Unused1 = 3, - Unused2 = 4, - Bluetooth = 5, - OlpcMesh = 6, - Wimax = 7, - Modem = 8, - InfiniBand = 9, - Bond = 10, - Vlan = 11, - Adsl = 12, - Bridge = 13, - Generic = 14, - Team = 15, - Tun = 16, - IpTunnel = 17, - MacVlan = 18, - VxLan = 19, - Veth = 20, - MacSec = 21, - Dummy = 22, - Ppp = 23, - OvsInterface = 24, - OvsPort = 25, - OvsBridge = 26, - Wpan = 27, - Lowpan = 28, - Wireguard = 29, - WifiP2P = 30, - Vrf = 31, - Loopback = 32, - Hsr = 33, - IpVlan = 34, - }; - 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 { - Q_OBJECT; - -public: - enum Enum : quint16 { - None = 0, - CipherWep40 = 1, - CipherWep104 = 2, - CipherTkip = 4, - CipherCcmp = 8, - Wpa = 16, - Rsn = 32, - Ap = 64, - Adhoc = 128, - FreqValid = 256, - Freq2Ghz = 512, - Freq5Ghz = 1024, - Freq6Ghz = 2048, - Mesh = 4096, - IbssRsn = 8192, - }; - Q_ENUM(Enum); -}; - -// Indicates the 802.11 mode an access point is currently in. -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NM80211Mode. -class NM80211Mode: public QObject { - Q_OBJECT; - -public: - enum Enum : quint8 { - Unknown = 0, - Adhoc = 1, - Infra = 2, - Ap = 3, - Mesh = 4, - }; - Q_ENUM(Enum); -}; - -// 802.11 access point flags. -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NM80211ApSecurityFlags. -class NM80211ApFlags: public QObject { - Q_OBJECT; - -public: - enum Enum : quint8 { - None = 0, - Privacy = 1, - Wps = 2, - WpsPbc = 4, - WpsPin = 8, - }; - Q_ENUM(Enum); -}; - -// 802.11 access point security and authentication flags. -// These flags describe the current system requirements of an access point as determined from the access point's beacon. -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NM80211ApSecurityFlags. -class NM80211ApSecurityFlags: public QObject { - Q_OBJECT; - -public: - enum Enum : quint16 { - None = 0, - PairWep40 = 1, - PairWep104 = 2, - PairTkip = 4, - PairCcmp = 8, - GroupWep40 = 16, - GroupWep104 = 32, - GroupTkip = 64, - GroupCcmp = 128, - KeyMgmtPsk = 256, - KeyMgmt8021x = 512, - KeyMgmtSae = 1024, - KeyMgmtOwe = 2048, - KeyMgmtOweTm = 4096, - KeyMgmtEapSuiteB192 = 8192, - }; - Q_ENUM(Enum); -}; - -// Indicates the state of a connection to a specific network while it is starting, connected, or disconnected from that network. -// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionState. -class NMConnectionState: public QObject { - Q_OBJECT; - -public: - enum Enum : quint8 { - Unknown = 0, - Activating = 1, - Activated = 2, - Deactivating = 3, - Deactivated = 4 - }; - Q_ENUM(Enum); -}; - -} // namespace qs::network diff --git a/src/network/nm/org.freedesktop.NetworkManager.AccessPoint.xml b/src/network/nm/org.freedesktop.NetworkManager.AccessPoint.xml deleted file mode 100644 index c5e7737..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.AccessPoint.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.Connection.Active.xml b/src/network/nm/org.freedesktop.NetworkManager.Connection.Active.xml deleted file mode 100644 index fa0e778..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.Connection.Active.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.Device.Wireless.xml b/src/network/nm/org.freedesktop.NetworkManager.Device.Wireless.xml deleted file mode 100644 index ccfe333..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.Device.Wireless.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.Device.xml b/src/network/nm/org.freedesktop.NetworkManager.Device.xml deleted file mode 100644 index 322635f..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.Device.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml b/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml deleted file mode 100644 index 0283847..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.Settings.Connection.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/src/network/nm/org.freedesktop.NetworkManager.xml b/src/network/nm/org.freedesktop.NetworkManager.xml deleted file mode 100644 index d4470ea..0000000 --- a/src/network/nm/org.freedesktop.NetworkManager.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/network/nm/utils.cpp b/src/network/nm/utils.cpp deleted file mode 100644 index 0be29e5..0000000 --- a/src/network/nm/utils.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "utils.hpp" - -// 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 "../wifi.hpp" -#include "dbus_types.hpp" -#include "enums.hpp" - -namespace qs::network { - -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(); - const QList proto = security["proto"].toList(); - - if (keyMgmt == "none") { - return WifiSecurityType::StaticWep; - } else if (keyMgmt == "ieee8021x") { - if (authAlg == "leap") { - return WifiSecurityType::Leap; - } else { - return WifiSecurityType::DynamicWep; - } - } else if (keyMgmt == "wpa-psk") { - if (proto.contains("wpa") && proto.contains("rsn")) return WifiSecurityType::WpaPsk; - return WifiSecurityType::Wpa2Psk; - } else if (keyMgmt == "wpa-eap") { - if (proto.contains("wpa") && proto.contains("rsn")) return WifiSecurityType::WpaEap; - return WifiSecurityType::Wpa2Eap; - } else if (keyMgmt == "sae") { - return WifiSecurityType::Sae; - } else if (keyMgmt == "wpa-eap-suite-b-192") { - return WifiSecurityType::Wpa3SuiteB192; - } - return WifiSecurityType::Open; -} - -bool deviceSupportsApCiphers( - NMWirelessCapabilities::Enum caps, - NM80211ApSecurityFlags::Enum apFlags, - WifiSecurityType::Enum type -) { - bool havePair = false; - bool haveGroup = false; - // Device needs to support at least one pairwise and one group cipher - - if (type == WifiSecurityType::StaticWep) { - // Static WEP only uses group ciphers - havePair = true; - } else { - if (caps & NMWirelessCapabilities::CipherWep40 && apFlags & NM80211ApSecurityFlags::PairWep40) { - havePair = true; - } - if (caps & NMWirelessCapabilities::CipherWep104 && apFlags & NM80211ApSecurityFlags::PairWep104) - { - havePair = true; - } - if (caps & NMWirelessCapabilities::CipherTkip && apFlags & NM80211ApSecurityFlags::PairTkip) { - havePair = true; - } - if (caps & NMWirelessCapabilities::CipherCcmp && apFlags & NM80211ApSecurityFlags::PairCcmp) { - havePair = true; - } - } - - if (caps & NMWirelessCapabilities::CipherWep40 && apFlags & NM80211ApSecurityFlags::GroupWep40) { - haveGroup = true; - } - if (caps & NMWirelessCapabilities::CipherWep104 && apFlags & NM80211ApSecurityFlags::GroupWep104) - { - haveGroup = true; - } - if (type != WifiSecurityType::StaticWep) { - if (caps & NMWirelessCapabilities::CipherTkip && apFlags & NM80211ApSecurityFlags::GroupTkip) { - haveGroup = true; - } - if (caps & NMWirelessCapabilities::CipherCcmp && apFlags & NM80211ApSecurityFlags::GroupCcmp) { - haveGroup = true; - } - } - - return (havePair && haveGroup); -} - -bool securityIsValid( - WifiSecurityType::Enum type, - NMWirelessCapabilities::Enum caps, - bool adhoc, - NM80211ApFlags::Enum apFlags, - NM80211ApSecurityFlags::Enum apWpa, - NM80211ApSecurityFlags::Enum apRsn -) { - switch (type) { - case WifiSecurityType::Open: - if (apFlags & NM80211ApFlags::Privacy) return false; - if (apWpa || apRsn) return false; - break; - case WifiSecurityType::Leap: - if (adhoc) return false; - case WifiSecurityType::StaticWep: - if (!(apFlags & NM80211ApFlags::Privacy)) return false; - if (apWpa || apRsn) { - if (!deviceSupportsApCiphers(caps, apWpa, WifiSecurityType::StaticWep)) { - if (!deviceSupportsApCiphers(caps, apRsn, WifiSecurityType::StaticWep)) return false; - } - } - break; - case WifiSecurityType::DynamicWep: - if (adhoc) return false; - if (apRsn || !(apFlags & NM80211ApFlags::Privacy)) return false; - if (apWpa) { - if (!(apWpa & NM80211ApSecurityFlags::KeyMgmt8021x)) return false; - if (!deviceSupportsApCiphers(caps, apWpa, WifiSecurityType::DynamicWep)) return false; - } - break; - case WifiSecurityType::WpaPsk: - if (adhoc) return false; - if (!(caps & NMWirelessCapabilities::Wpa)) return false; - if (apWpa & NM80211ApSecurityFlags::KeyMgmtPsk) { - if (apWpa & NM80211ApSecurityFlags::PairTkip && caps & NMWirelessCapabilities::CipherTkip) { - return true; - } - if (apWpa & NM80211ApSecurityFlags::PairCcmp && caps & NMWirelessCapabilities::CipherCcmp) { - return true; - } - } - return false; - case WifiSecurityType::Wpa2Psk: - if (!(caps & NMWirelessCapabilities::Rsn)) return false; - if (adhoc) { - if (!(caps & NMWirelessCapabilities::IbssRsn)) return false; - if (apRsn & NM80211ApSecurityFlags::PairCcmp && caps & NMWirelessCapabilities::CipherCcmp) { - return true; - } - } else { - if (apRsn & NM80211ApSecurityFlags::KeyMgmtPsk) { - if (apRsn & NM80211ApSecurityFlags::PairTkip && caps & NMWirelessCapabilities::CipherTkip) { - return true; - } - if (apRsn & NM80211ApSecurityFlags::PairCcmp && caps & NMWirelessCapabilities::CipherCcmp) { - return true; - } - } - } - return false; - case WifiSecurityType::WpaEap: - if (adhoc) return false; - if (!(caps & NMWirelessCapabilities::Wpa)) return false; - if (!(apWpa & NM80211ApSecurityFlags::KeyMgmt8021x)) return false; - if (!deviceSupportsApCiphers(caps, apWpa, WifiSecurityType::WpaEap)) return false; - break; - case WifiSecurityType::Wpa2Eap: - if (adhoc) return false; - if (!(caps & NMWirelessCapabilities::Rsn)) return false; - if (!(apRsn & NM80211ApSecurityFlags::KeyMgmt8021x)) return false; - if (!deviceSupportsApCiphers(caps, apRsn, WifiSecurityType::Wpa2Eap)) return false; - break; - case WifiSecurityType::Sae: - if (!(caps & NMWirelessCapabilities::Rsn)) return false; - if (adhoc) { - if (!(caps & NMWirelessCapabilities::IbssRsn)) return false; - if (apRsn & NM80211ApSecurityFlags::PairCcmp && caps & NMWirelessCapabilities::CipherCcmp) { - return true; - } - } else { - if (apRsn & NM80211ApSecurityFlags::KeyMgmtSae) { - if (apRsn & NM80211ApSecurityFlags::PairTkip && caps & NMWirelessCapabilities::CipherTkip) { - return true; - } - if (apRsn & NM80211ApSecurityFlags::PairCcmp && caps & NMWirelessCapabilities::CipherCcmp) { - return true; - } - } - } - return false; - case WifiSecurityType::Owe: - if (adhoc) return false; - if (!(caps & NMWirelessCapabilities::Rsn)) return false; - if (!(apRsn & NM80211ApSecurityFlags::KeyMgmtOwe) - && !(apRsn & NM80211ApSecurityFlags::KeyMgmtOweTm)) - { - return false; - } - break; - case WifiSecurityType::Wpa3SuiteB192: - if (adhoc) return false; - if (!(caps & NMWirelessCapabilities::Rsn)) return false; - if (!(apRsn & NM80211ApSecurityFlags::KeyMgmtEapSuiteB192)) return false; - break; - default: return false; - } - return true; -} - -WifiSecurityType::Enum findBestWirelessSecurity( - NMWirelessCapabilities::Enum caps, - bool adHoc, - NM80211ApFlags::Enum apFlags, - NM80211ApSecurityFlags::Enum apWpa, - NM80211ApSecurityFlags::Enum apRsn -) { - // Loop through security types from most to least secure since the enum - // values are sequential and in priority order (0-10, excluding Unknown=11) - for (int i = WifiSecurityType::Wpa3SuiteB192; i <= WifiSecurityType::Open; ++i) { - auto type = static_cast(i); - if (securityIsValid(type, caps, adHoc, apFlags, apWpa, apRsn)) { - return type; - } - } - return WifiSecurityType::Unknown; -} - -// NOLINTBEGIN -QDateTime clockBootTimeToDateTime(qint64 clockBootTime) { - clockid_t clkId = CLOCK_BOOTTIME; - struct timespec tp {}; - - const QDateTime now = QDateTime::currentDateTime(); - int r = clock_gettime(clkId, &tp); - if (r == -1 && errno == EINVAL) { - clkId = CLOCK_MONOTONIC; - r = clock_gettime(clkId, &tp); - } - - // Convert to milliseconds - const qint64 nowInMs = tp.tv_sec * 1000 + tp.tv_nsec / 1000000; - - // Return a QDateTime of the millisecond diff - const qint64 offset = clockBootTime - nowInMs; - return QDateTime::fromMSecsSinceEpoch(now.toMSecsSinceEpoch() + offset); -} -// NOLINTEND - -} // namespace qs::network diff --git a/src/network/nm/utils.hpp b/src/network/nm/utils.hpp deleted file mode 100644 index ce8b784..0000000 --- a/src/network/nm/utils.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "../wifi.hpp" -#include "dbus_types.hpp" -#include "enums.hpp" - -namespace qs::network { - -WifiSecurityType::Enum securityFromConnectionSettings(const ConnectionSettingsMap& settings); - -bool deviceSupportsApCiphers( - NMWirelessCapabilities::Enum caps, - NM80211ApSecurityFlags::Enum apFlags, - WifiSecurityType::Enum type -); - -// In sync with NetworkManager/libnm-core/nm-utils.c:nm_utils_security_valid() -// Given a set of device capabilities, and a desired security type to check -// against, determines whether the combination of device, desired security type, -// and AP capabilities intersect. -bool securityIsValid( - WifiSecurityType::Enum type, - NMWirelessCapabilities::Enum caps, - bool adhoc, - NM80211ApFlags::Enum apFlags, - NM80211ApSecurityFlags::Enum apWpa, - NM80211ApSecurityFlags::Enum apRsn -); - -WifiSecurityType::Enum findBestWirelessSecurity( - NMWirelessCapabilities::Enum caps, - bool adHoc, - NM80211ApFlags::Enum apFlags, - NM80211ApSecurityFlags::Enum apWpa, - NM80211ApSecurityFlags::Enum apRsn -); - -QDateTime clockBootTimeToDateTime(qint64 clockBootTime); - -} // namespace qs::network diff --git a/src/network/nm/wireless.cpp b/src/network/nm/wireless.cpp deleted file mode 100644 index 9dff14b..0000000 --- a/src/network/nm/wireless.cpp +++ /dev/null @@ -1,457 +0,0 @@ -#include "wireless.hpp" -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../core/logcat.hpp" -#include "../../dbus/properties.hpp" -#include "../network.hpp" -#include "../wifi.hpp" -#include "accesspoint.hpp" -#include "connection.hpp" -#include "dbus_nm_wireless.h" -#include "dbus_types.hpp" -#include "device.hpp" -#include "enums.hpp" -#include "utils.hpp" - -namespace qs::network { -using namespace qs::dbus; - -namespace { -QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg); -} - -NMWirelessNetwork::NMWirelessNetwork(QString ssid, QObject* parent) - : QObject(parent) - , mSsid(std::move(ssid)) - , bKnown(false) - , bSecurity(WifiSecurityType::Unknown) - , bReason(NMConnectionStateReason::None) - , bState(NMConnectionState::Deactivated) {} - -void NMWirelessNetwork::updateReferenceConnection() { - // If the network has no connections, the reference is 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 it as the reference. - if (this->mActiveConnection) { - 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 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->mReferenceConn != selectedConn) { - this->mReferenceConn = selectedConn; - this->bSecurity.setBinding([selectedConn]() { return selectedConn->security(); }); - } -} - -void NMWirelessNetwork::updateReferenceAp() { - // If the network has no APs, the reference is a nullptr. - if (this->mAccessPoints.isEmpty()) { - this->mReferenceAp = nullptr; - this->bSignalStrength = 0; - return; - } - - // Otherwise, choose the AP with the strongest signal. - NMAccessPoint* selectedAp = nullptr; - for (auto* ap: this->mAccessPoints.values()) { - // Always prefer the active AP. - if (ap->path() == this->bActiveApPath) { - selectedAp = ap; - break; - } - if (!selectedAp || ap->signalStrength() > selectedAp->signalStrength()) { - selectedAp = ap; - } - } - if (this->mReferenceAp != selectedAp) { - this->mReferenceAp = selectedAp; - this->bSignalStrength.setBinding([selectedAp]() { return selectedAp->signalStrength(); }); - // Reference AP is used for security when there's no connection settings. - if (!this->mReferenceConn) { - this->bSecurity.setBinding([selectedAp]() { return selectedAp->security(); }); - } - } -} - -void NMWirelessNetwork::addAccessPoint(NMAccessPoint* ap) { - if (this->mAccessPoints.contains(ap->path())) return; - this->mAccessPoints.insert(ap->path(), ap); - auto onDestroyed = [this, ap]() { - if (this->mAccessPoints.take(ap->path())) { - this->updateReferenceAp(); - if (this->mAccessPoints.isEmpty() && this->mConnections.isEmpty()) emit this->disappeared(); - } - }; - // clang-format off - QObject::connect(ap, &NMAccessPoint::signalStrengthChanged, this, &NMWirelessNetwork::updateReferenceAp); - QObject::connect(ap, &NMAccessPoint::destroyed, this, onDestroyed); - // clang-format on - this->updateReferenceAp(); -}; - -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(); - } - }; - // 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->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->updateReferenceConnection(); - this->bState = NMConnectionState::Deactivated; - this->bReason = NMConnectionStateReason::None; - } - }; - QObject::connect(active, &NMActiveConnection::destroyed, this, onDestroyed); - this->updateReferenceConnection(); -}; - -void NMWirelessNetwork::forget() { - if (this->mConnections.isEmpty()) return; - for (auto* conn: this->mConnections.values()) { - conn->forget(); - } -} - -NMWirelessDevice::NMWirelessDevice(const QString& path, QObject* parent) - : NMDevice(path, parent) - , mScanTimer(this) { - this->wirelessProxy = new DBusNMWirelessProxy( - "org.freedesktop.NetworkManager", - path, - QDBusConnection::systemBus(), - this - ); - - if (!this->wirelessProxy->isValid()) { - qCWarning(logNetworkManager) << "Cannot create DBus interface for wireless device at" << path; - return; - } - - QObject::connect( - &this->wirelessProperties, - &DBusPropertyGroup::getAllFinished, - this, - &NMWirelessDevice::initWireless, - Qt::SingleShotConnection - ); - - QObject::connect(&this->mScanTimer, &QTimer::timeout, this, &NMWirelessDevice::onScanTimeout); - this->mScanTimer.setSingleShot(true); - - this->wirelessProperties.setInterface(this->wirelessProxy); - this->wirelessProperties.updateAllViaGetAll(); -} - -void NMWirelessDevice::initWireless() { - // clang-format off - 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::connectionLoaded, this, &NMWirelessDevice::onConnectionLoaded); - QObject::connect(this, &NMWirelessDevice::activeConnectionLoaded, this, &NMWirelessDevice::onActiveConnectionLoaded); - QObject::connect(this, &NMWirelessDevice::scanningChanged, this, &NMWirelessDevice::onScanningChanged); - // clang-format on - this->registerAccessPoints(); -} - -void NMWirelessDevice::onAccessPointAdded(const QDBusObjectPath& path) { - this->registerAccessPoint(path.path()); -} - -void NMWirelessDevice::onAccessPointRemoved(const QDBusObjectPath& path) { - auto* ap = this->mAccessPoints.take(path.path()); - if (!ap) { - qCDebug(logNetworkManager) << "Sent removal signal for" << path.path() - << "which is not registered."; - return; - } - delete ap; -} - -void NMWirelessDevice::onAccessPointLoaded(NMAccessPoint* ap) { - const QString ssid = ap->ssid(); - if (!ssid.isEmpty()) { - auto mode = ap->mode(); - if (mode == NM80211Mode::Infra) { - auto* net = this->mNetworks.value(ssid); - if (!net) net = this->registerNetwork(ssid); - net->addAccessPoint(ap); - } - } -} - -void NMWirelessDevice::onConnectionLoaded(NMConnectionSettings* conn) { - const ConnectionSettingsMap& settings = conn->settings(); - // Filter connections that aren't wireless or have missing settings - 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 = 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->addConnection(conn); - - // Check for active connections that loaded before their respective connection settings - auto* active = this->activeConnection(); - if (active && conn->path() == active->connection().path()) { - net->addActiveConnection(active); - } - } - // TODO: Create hotspots when mode == "ap" -} - -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* conn: net->connections()) { - if (activeConnPath == conn->path()) { - net->addActiveConnection(active); - return; - } - } - } -} - -void NMWirelessDevice::onScanTimeout() { - const QDateTime now = QDateTime::currentDateTime(); - const QDateTime lastScan = this->bLastScan; - const QDateTime lastScanRequest = this->mLastScanRequest; - - if (lastScan.isValid() && lastScan.msecsTo(now) < this->mScanIntervalMs) { - // Rate limit if backend last scan property updated within the interval - auto diff = static_cast(this->mScanIntervalMs - lastScan.msecsTo(now)); - this->mScanTimer.start(diff); - } else if (lastScanRequest.isValid() && lastScanRequest.msecsTo(now) < this->mScanIntervalMs) { - // Rate limit if frontend changes scanner state within the interval - auto diff = static_cast(this->mScanIntervalMs - lastScanRequest.msecsTo(now)); - this->mScanTimer.start(diff); - } else { - this->wirelessProxy->RequestScan({}); - this->mLastScanRequest = now; - this->mScanTimer.start(this->mScanIntervalMs); - } -} - -void NMWirelessDevice::onScanningChanged(bool scanning) { - scanning ? this->onScanTimeout() : this->mScanTimer.stop(); -} - -void NMWirelessDevice::registerAccessPoints() { - auto pending = this->wirelessProxy->GetAllAccessPoints(); - auto* call = new QDBusPendingCallWatcher(pending, this); - - auto responseCallback = [this](QDBusPendingCallWatcher* call) { - const QDBusPendingReply> reply = *call; - - if (reply.isError()) { - qCWarning(logNetworkManager) - << "Failed to get all access points: " << reply.error().message(); - } else { - for (const QDBusObjectPath& devicePath: reply.value()) { - this->registerAccessPoint(devicePath.path()); - } - } - - delete call; - }; - - QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); -} - -void NMWirelessDevice::registerAccessPoint(const QString& path) { - if (this->mAccessPoints.contains(path)) { - qCDebug(logNetworkManager) << "Skipping duplicate registration of access point" << path; - return; - } - - auto* ap = new NMAccessPoint(path, this); - - if (!ap->isValid()) { - qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; - delete ap; - return; - } - - this->mAccessPoints.insert(path, ap); - QObject::connect( - ap, - &NMAccessPoint::loaded, - this, - [this, ap]() { emit this->accessPointLoaded(ap); }, - Qt::SingleShotConnection - ); - ap->bindableSecurity().setBinding([this, ap]() { - return findBestWirelessSecurity( - this->bCapabilities, - ap->mode() == NM80211Mode::Adhoc, - ap->flags(), - ap->wpaFlags(), - ap->rsnFlags() - ); - }); -} - -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(); }); - QObject::connect(net, &NMWirelessNetwork::disappeared, this, &NMWirelessDevice::removeNetwork); - QObject::connect(net, &NMWirelessNetwork::visibilityChanged, this, onVisibilityChanged); - - this->mNetworks.insert(ssid, net); - if (net->visible()) this->registerFrontendNetwork(net); - return net; -} - -void NMWirelessDevice::registerFrontendNetwork(NMWirelessNetwork* net) { - auto ssid = net->ssid(); - auto* frontendNet = new WifiNetwork(ssid, net); - - // Bind WifiNetwork to NMWirelessNetwork - auto translateSignal = [net]() { return net->signalStrength() / 100.0; }; - auto translateState = [net]() { return net->state() == NMConnectionState::Activated; }; - 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(frontendNet, &WifiNetwork::requestConnect, this, [this, net]() { - if (net->referenceConnection()) { - emit this->activateConnection( - QDBusObjectPath(net->referenceConnection()->path()), - QDBusObjectPath(this->path()) - ); - return; - } - if (net->referenceAp()) { - emit this->addAndActivateConnection( - ConnectionSettingsMap(), - QDBusObjectPath(this->path()), - QDBusObjectPath(net->referenceAp()->path()) - ); - } - }); - - QObject::connect( - frontendNet, - &WifiNetwork::requestDisconnect, - this, - &NMWirelessDevice::disconnect - ); - - QObject::connect(frontendNet, &WifiNetwork::requestForget, net, &NMWirelessNetwork::forget); - - this->mFrontendNetworks.insert(ssid, frontendNet); - emit this->networkAdded(frontendNet); -} - -void NMWirelessDevice::removeFrontendNetwork(NMWirelessNetwork* net) { - auto* frontendNet = this->mFrontendNetworks.take(net->ssid()); - if (frontendNet) { - emit this->networkRemoved(frontendNet); - frontendNet->deleteLater(); - } -} - -void NMWirelessDevice::removeNetwork() { - auto* net = qobject_cast(this->sender()); - if (this->mNetworks.take(net->ssid())) { - this->removeFrontendNetwork(net); - delete net; - }; -} - -bool NMWirelessDevice::isValid() const { - return this->NMDevice::isValid() && (this->wirelessProxy && this->wirelessProxy->isValid()); -} - -} // namespace qs::network - -namespace qs::dbus { - -DBusResult -DBusDataTransform::fromWire(quint32 wire) { - return DBusResult(static_cast(wire)); -} - -DBusResult DBusDataTransform::fromWire(qint64 wire) { - return DBusResult(qs::network::clockBootTimeToDateTime(wire)); -} - -} // namespace qs::dbus diff --git a/src/network/nm/wireless.hpp b/src/network/nm/wireless.hpp deleted file mode 100644 index fe4010e..0000000 --- a/src/network/nm/wireless.hpp +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "../wifi.hpp" -#include "accesspoint.hpp" -#include "connection.hpp" -#include "dbus_nm_wireless.h" -#include "device.hpp" -#include "enums.hpp" - -namespace qs::dbus { -template <> -struct DBusDataTransform { - using Wire = quint32; - using Data = qs::network::NMWirelessCapabilities::Enum; - static DBusResult fromWire(Wire wire); -}; - -template <> -struct DBusDataTransform { - using Wire = qint64; - using Data = QDateTime; - static DBusResult fromWire(Wire wire); -}; - -} // namespace qs::dbus -namespace qs::network { - -// NMWirelessNetwork aggregates all related NMActiveConnection, NMAccessPoint, and NMConnectionSetting objects. -class NMWirelessNetwork: public QObject { - Q_OBJECT; - -public: - explicit NMWirelessNetwork(QString ssid, QObject* parent = nullptr); - - void addAccessPoint(NMAccessPoint* ap); - void addConnection(NMConnectionSettings* conn); - void addActiveConnection(NMActiveConnection* active); - void forget(); - - [[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 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 capabilitiesChanged(NMWirelessCapabilities::Enum caps); - void activeApPathChanged(QString path); - -private: - void updateReferenceAp(); - void updateReferenceConnection(); - - QString mSsid; - QHash mAccessPoints; - QHash mConnections; - NMAccessPoint* mReferenceAp = nullptr; - NMConnectionSettings* mReferenceConn = nullptr; - NMActiveConnection* mActiveConnection = nullptr; - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, bool, bVisible, &NMWirelessNetwork::visibilityChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, bool, bKnown, &NMWirelessNetwork::knownChanged); - 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, quint8, bSignalStrength, &NMWirelessNetwork::signalStrengthChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, QString, bActiveApPath, &NMWirelessNetwork::activeApPathChanged); - // clang-format on -}; - -// Proxy of a /org/freedesktop/NetworkManager/Device/* object. -// Extends NMDevice to also include members from the org.freedesktop.NetworkManager.Device.Wireless interface -// Owns the lifetime of NMAccessPoints(s), NMWirelessNetwork(s), frontend WifiNetwork(s). -class NMWirelessDevice: public NMDevice { - Q_OBJECT; - -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; }; - -signals: - void accessPointLoaded(NMAccessPoint* ap); - void accessPointRemoved(NMAccessPoint* ap); - void networkAdded(WifiNetwork* net); - void networkRemoved(WifiNetwork* net); - void lastScanChanged(QDateTime lastScan); - void scanningChanged(bool scanning); - void capabilitiesChanged(NMWirelessCapabilities::Enum caps); - void activeAccessPointChanged(const QDBusObjectPath& path); - void modeChanged(NM80211Mode::Enum mode); - -private slots: - void onAccessPointAdded(const QDBusObjectPath& path); - void onAccessPointRemoved(const QDBusObjectPath& path); - void onAccessPointLoaded(NMAccessPoint* ap); - void onConnectionLoaded(NMConnectionSettings* conn); - void onActiveConnectionLoaded(NMActiveConnection* active); - void onScanTimeout(); - void onScanningChanged(bool scanning); - -private: - void registerAccessPoint(const QString& path); - void registerFrontendNetwork(NMWirelessNetwork* net); - void removeFrontendNetwork(NMWirelessNetwork* net); - void removeNetwork(); - bool checkVisibility(WifiNetwork* net); - void registerAccessPoints(); - void initWireless(); - NMWirelessNetwork* registerNetwork(const QString& ssid); - - QHash mAccessPoints; - QHash mNetworks; - QHash mFrontendNetworks; - - QDateTime mLastScanRequest; - QTimer mScanTimer; - qint32 mScanIntervalMs = 10001; - - // clang-format off - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessDevice, bool, bScanning, &NMWirelessDevice::scanningChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessDevice, QDateTime, bLastScan, &NMWirelessDevice::lastScanChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessDevice, NMWirelessCapabilities::Enum, bCapabilities, &NMWirelessDevice::capabilitiesChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessDevice, QDBusObjectPath, bActiveAccessPoint, &NMWirelessDevice::activeAccessPointChanged); - Q_OBJECT_BINDABLE_PROPERTY(NMWirelessDevice, NM80211Mode::Enum, bMode, &NMWirelessDevice::modeChanged); - - QS_DBUS_BINDABLE_PROPERTY_GROUP(NMWireless, wirelessProperties); - QS_DBUS_PROPERTY_BINDING(NMWirelessDevice, pLastScan, bLastScan, wirelessProperties, "LastScan"); - QS_DBUS_PROPERTY_BINDING(NMWirelessDevice, pCapabilities, bCapabilities, wirelessProperties, "WirelessCapabilities"); - QS_DBUS_PROPERTY_BINDING(NMWirelessDevice, pActiveAccessPoint, bActiveAccessPoint, wirelessProperties, "ActiveAccessPoint"); - QS_DBUS_PROPERTY_BINDING(NMWirelessDevice, pMode, bMode, wirelessProperties, "Mode"); - // clang-format on - - DBusNMWirelessProxy* wirelessProxy = nullptr; -}; - -} // namespace qs::network diff --git a/src/network/test/manual/network.qml b/src/network/test/manual/network.qml deleted file mode 100644 index 0fd0f72..0000000 --- a/src/network/test/manual/network.qml +++ /dev/null @@ -1,155 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import Quickshell.Widgets -import Quickshell.Networking - -FloatingWindow { - color: contentItem.palette.window - - ColumnLayout { - anchors.fill: parent - anchors.margins: 5 - - 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 - } - } - } - - 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)})` } - } - 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 - } - } - - 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 - }) - } - - WrapperRectangle { - 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 - } - } - 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 deleted file mode 100644 index dcd20f6..0000000 --- a/src/network/wifi.cpp +++ /dev/null @@ -1,139 +0,0 @@ -#include "wifi.hpp" -#include - -#include -#include -#include -#include -#include -#include - -#include "../core/logcat.hpp" -#include "device.hpp" -#include "network.hpp" - -namespace qs::network { - -namespace { -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::connect() { - if (this->bConnected) { - qCCritical(logWifi) << this << "is already connected."; - return; - } - - 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) { - if (this->bScannerEnabled == enabled) return; - this->bScannerEnabled = enabled; -} - -void WifiDevice::networkAdded(WifiNetwork* net) { this->mNetworks.insertObject(net); } -void WifiDevice::networkRemoved(WifiNetwork* net) { this->mNetworks.removeObject(net); } - -} // namespace qs::network - -QDebug operator<<(QDebug debug, const qs::network::WifiNetwork* network) { - auto saver = QDebugStateSaver(debug); - - if (network) { - debug.nospace() << "WifiNetwork(" << static_cast(network) - << ", name=" << network->name() << ")"; - } else { - debug << "WifiNetwork(nullptr)"; - } - - return debug; -} - -QDebug operator<<(QDebug debug, const qs::network::WifiDevice* device) { - auto saver = QDebugStateSaver(debug); - - if (device) { - debug.nospace() << "WifiDevice(" << static_cast(device) - << ", name=" << device->name() << ")"; - } else { - debug << "WifiDevice(nullptr)"; - } - - return debug; -} diff --git a/src/network/wifi.hpp b/src/network/wifi.hpp deleted file mode 100644 index 15b093d..0000000 --- a/src/network/wifi.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -#include "../core/model.hpp" -#include "device.hpp" -#include "network.hpp" - -namespace qs::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; - QML_UNCREATABLE("WifiNetwork can only be acquired through WifiDevice"); - // 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 wifi network. - /// - /// > [!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: - 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 -}; - -///! Wireless variant of a NetworkDevice. -class WifiDevice: public NetworkDevice { - Q_OBJECT; - QML_ELEMENT; - QML_UNCREATABLE(""); - - // clang-format off - /// 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. - /// When enabled, the scanner populates the device with an active list of available wifi networks. - Q_PROPERTY(bool scannerEnabled READ scannerEnabled WRITE setScannerEnabled NOTIFY scannerEnabledChanged BINDABLE bindableScannerEnabled); - /// The 802.11 mode the device is in. - Q_PROPERTY(WifiDeviceMode::Enum mode READ default NOTIFY modeChanged BINDABLE bindableMode); - // clang-format on - -public: - explicit WifiDevice(QObject* parent = nullptr); - - 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; }; - void setScannerEnabled(bool enabled); - QBindable bindableMode() { return &this->bMode; } - -signals: - void modeChanged(); - void scannerEnabledChanged(bool enabled); - -private: - ObjectModel mNetworks {this}; - Q_OBJECT_BINDABLE_PROPERTY(WifiDevice, bool, bScannerEnabled, &WifiDevice::scannerEnabledChanged); - Q_OBJECT_BINDABLE_PROPERTY(WifiDevice, WifiDeviceMode::Enum, bMode, &WifiDevice::modeChanged); -}; - -}; // namespace qs::network - -QDebug operator<<(QDebug debug, const qs::network::WifiNetwork* network); -QDebug operator<<(QDebug debug, const qs::network::WifiDevice* device); diff --git a/src/wayland/popupanchor.cpp b/src/wayland/popupanchor.cpp index 14e1923..cbbccae 100644 --- a/src/wayland/popupanchor.cpp +++ b/src/wayland/popupanchor.cpp @@ -16,6 +16,7 @@ using XdgPositioner = QtWayland::xdg_positioner; using qs::wayland::xdg_shell::XdgWmBase; void WaylandPopupPositioner::reposition(PopupAnchor* anchor, QWindow* window, bool onlyIfDirty) { + auto* waylandWindow = dynamic_cast(window->handle()); auto* popupRole = waylandWindow ? waylandWindow->surfaceRole<::xdg_popup>() : nullptr; diff --git a/src/window/popupwindow.cpp b/src/window/popupwindow.cpp index 0b35948..a1ae448 100644 --- a/src/window/popupwindow.cpp +++ b/src/window/popupwindow.cpp @@ -12,74 +12,29 @@ ProxyPopupWindow::ProxyPopupWindow(QObject* parent): ProxyWindowBase(parent) { this->mVisible = false; - // clang-format off - QObject::connect(&this->mAnchor, &PopupAnchor::windowChanged, this, &ProxyPopupWindow::onParentWindowChanged); + QObject::connect(&this->mAnchor, &PopupAnchor::windowChanged, this, &ProxyPopupWindow::parentWindowChanged); QObject::connect(&this->mAnchor, &PopupAnchor::windowRectChanged, this, &ProxyPopupWindow::reposition); QObject::connect(&this->mAnchor, &PopupAnchor::edgesChanged, this, &ProxyPopupWindow::reposition); QObject::connect(&this->mAnchor, &PopupAnchor::gravityChanged, this, &ProxyPopupWindow::reposition); QObject::connect(&this->mAnchor, &PopupAnchor::adjustmentChanged, this, &ProxyPopupWindow::reposition); + QObject::connect(&this->mAnchor, &PopupAnchor::backingWindowVisibilityChanged, this, &ProxyPopupWindow::onParentUpdated); // clang-format on - - this->bTargetVisible.setBinding([this] { - auto* window = this->mAnchor.bindableProxyWindow().value(); - - if (window == this) { - qmlWarning(this) << "Anchor assigned to current window"; - return false; - } - - if (!window) return false; - - if (!this->bWantsVisible) return false; - return window->bindableBackerVisibility().value(); - }); -} - -void ProxyPopupWindow::targetVisibleChanged() { - this->ProxyWindowBase::setVisible(this->bTargetVisible); } void ProxyPopupWindow::completeWindow() { this->ProxyWindowBase::completeWindow(); // clang-format off - QObject::connect(this, &ProxyWindowBase::closed, this, &ProxyPopupWindow::onClosed); + QObject::connect(this->window, &QWindow::visibleChanged, this, &ProxyPopupWindow::onVisibleChanged); QObject::connect(this->window, &QWindow::widthChanged, this, &ProxyPopupWindow::reposition); QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyPopupWindow::reposition); // clang-format on - auto* bw = this->mAnchor.backingWindow(); - - if (bw && PopupPositioner::instance()->shouldRepositionOnMove()) { - QObject::connect(bw, &QWindow::xChanged, this, &ProxyPopupWindow::reposition); - QObject::connect(bw, &QWindow::yChanged, this, &ProxyPopupWindow::reposition); - QObject::connect(bw, &QWindow::widthChanged, this, &ProxyPopupWindow::reposition); - QObject::connect(bw, &QWindow::heightChanged, this, &ProxyPopupWindow::reposition); - } - - this->window->setTransientParent(bw); - this->window->setFlag(this->bWantsGrab ? Qt::Popup : Qt::ToolTip); - - this->mAnchor.markDirty(); - PopupPositioner::instance()->reposition(&this->mAnchor, this->window); + this->window->setFlag(Qt::ToolTip); } -void ProxyPopupWindow::postCompleteWindow() { - this->ProxyWindowBase::setVisible(this->bTargetVisible); -} - -void ProxyPopupWindow::onClosed() { this->bWantsVisible = false; } - -void ProxyPopupWindow::onParentWindowChanged() { - // recreate for new parent - if (this->bTargetVisible && this->isVisibleDirect()) { - this->ProxyWindowBase::setVisibleDirect(false); - this->ProxyWindowBase::setVisibleDirect(true); - } - - emit this->parentWindowChanged(); -} +void ProxyPopupWindow::postCompleteWindow() { this->updateTransientParent(); } void ProxyPopupWindow::setParentWindow(QObject* parent) { qmlWarning(this) << "PopupWindow.parentWindow is deprecated. Use PopupWindow.anchor.window."; @@ -88,13 +43,60 @@ void ProxyPopupWindow::setParentWindow(QObject* parent) { QObject* ProxyPopupWindow::parentWindow() const { return this->mAnchor.window(); } +void ProxyPopupWindow::updateTransientParent() { + auto* bw = this->mAnchor.backingWindow(); + + if (this->window != nullptr && bw != this->window->transientParent()) { + if (this->window->transientParent()) { + QObject::disconnect(this->window->transientParent(), nullptr, this, nullptr); + } + + if (bw && PopupPositioner::instance()->shouldRepositionOnMove()) { + QObject::connect(bw, &QWindow::xChanged, this, &ProxyPopupWindow::reposition); + QObject::connect(bw, &QWindow::yChanged, this, &ProxyPopupWindow::reposition); + QObject::connect(bw, &QWindow::widthChanged, this, &ProxyPopupWindow::reposition); + QObject::connect(bw, &QWindow::heightChanged, this, &ProxyPopupWindow::reposition); + } + + this->window->setTransientParent(bw); + } + + this->updateVisible(); +} + +void ProxyPopupWindow::onParentUpdated() { this->updateTransientParent(); } + void ProxyPopupWindow::setScreen(QuickshellScreenInfo* /*unused*/) { qmlWarning( this ) << "Cannot set screen of popup window, as that is controlled by the parent window"; } -void ProxyPopupWindow::setVisible(bool visible) { this->bWantsVisible = visible; } +void ProxyPopupWindow::setVisible(bool visible) { + if (visible == this->wantsVisible) return; + this->wantsVisible = visible; + this->updateVisible(); +} + +void ProxyPopupWindow::updateVisible() { + auto target = this->wantsVisible && this->mAnchor.window() != nullptr + && this->mAnchor.proxyWindow()->isVisibleDirect(); + + if (target && this->window != nullptr && !this->window->isVisible()) { + PopupPositioner::instance()->reposition(&this->mAnchor, this->window); + } + + this->ProxyWindowBase::setVisible(target); +} + +void ProxyPopupWindow::onVisibleChanged() { + // If the window was made invisible without its parent becoming invisible + // the compositor probably destroyed it. Without this the window won't ever + // be able to become visible again. + if (this->window->transientParent() && this->window->transientParent()->isVisible()) { + this->wantsVisible = this->window->isVisible(); + } +} void ProxyPopupWindow::setRelativeX(qint32 x) { qmlWarning(this) << "PopupWindow.relativeX is deprecated. Use PopupWindow.anchor.rect.x."; @@ -142,5 +144,3 @@ void ProxyPopupWindow::onPolished() { } } } - -bool ProxyPopupWindow::deleteOnInvisible() const { return true; } diff --git a/src/window/popupwindow.hpp b/src/window/popupwindow.hpp index d95eac0..e00495c 100644 --- a/src/window/popupwindow.hpp +++ b/src/window/popupwindow.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -76,15 +75,6 @@ class ProxyPopupWindow: public ProxyWindowBase { /// /// The popup will not be shown until @@anchor is valid, regardless of this property. QSDOC_PROPERTY_OVERRIDE(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged); - /// If true, the popup window will be dismissed and @@visible will change to false - /// if the user clicks outside of the popup or it is otherwise closed. - /// - /// > [!WARNING] Changes to this property while the window is open will only take - /// > effect after the window is hidden and shown again. - /// - /// > [!NOTE] Under Hyprland, @@Quickshell.Hyprland.HyprlandFocusGrab provides more advanced - /// > functionality such as detecting clicks outside without closing the popup. - Q_PROPERTY(bool grabFocus READ default WRITE default NOTIFY grabFocusChanged BINDABLE bindableGrabFocus); /// The screen that the window currently occupies. /// /// This may be modified to move the window to the given screen. @@ -98,7 +88,6 @@ public: void completeWindow() override; void postCompleteWindow() override; void onPolished() override; - bool deleteOnInvisible() const override; void setScreen(QuickshellScreenInfo* screen) override; void setVisible(bool visible) override; @@ -112,37 +101,24 @@ public: [[nodiscard]] qint32 relativeY() const; void setRelativeY(qint32 y); - [[nodiscard]] QBindable bindableGrabFocus() { return &this->bWantsGrab; } - [[nodiscard]] PopupAnchor* anchor(); signals: void parentWindowChanged(); void relativeXChanged(); void relativeYChanged(); - void grabFocusChanged(); private slots: - void onParentWindowChanged(); - void onClosed(); + void onVisibleChanged(); + void onParentUpdated(); void reposition(); private: - void targetVisibleChanged(); - QQuickWindow* parentBackingWindow(); + void updateTransientParent(); + void updateVisible(); PopupAnchor mAnchor {this}; + bool wantsVisible = false; bool pendingReposition = false; - - Q_OBJECT_BINDABLE_PROPERTY(ProxyPopupWindow, bool, bWantsVisible); - - Q_OBJECT_BINDABLE_PROPERTY( - ProxyPopupWindow, - bool, - bTargetVisible, - &ProxyPopupWindow::targetVisibleChanged - ); - - Q_OBJECT_BINDABLE_PROPERTY(ProxyPopupWindow, bool, bWantsGrab); }; diff --git a/src/window/proxywindow.cpp b/src/window/proxywindow.cpp index 3cc4378..ea2904b 100644 --- a/src/window/proxywindow.cpp +++ b/src/window/proxywindow.cpp @@ -57,10 +57,9 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent) ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(true); } void ProxyWindowBase::onReload(QObject* oldInstance) { - if (this->mVisible) this->window = this->retrieveWindow(oldInstance); + this->window = this->retrieveWindow(oldInstance); auto wasVisible = this->window != nullptr && this->window->isVisible(); - - if (this->mVisible) this->ensureQWindow(); + this->ensureQWindow(); // The qml engine will leave the WindowInterface as owner of everything // nested in an item, so we have to make sure the interface's children @@ -77,21 +76,17 @@ void ProxyWindowBase::onReload(QObject* oldInstance) { Reloadable::reloadChildrenRecursive(this, oldInstance); - if (this->mVisible) { - this->connectWindow(); - this->completeWindow(); - } + this->connectWindow(); + this->completeWindow(); this->reloadComplete = true; - if (this->mVisible) { - emit this->windowConnected(); - this->postCompleteWindow(); + emit this->windowConnected(); + this->postCompleteWindow(); - if (wasVisible && this->isVisibleDirect()) { - this->bBackerVisibility = true; - this->onExposed(); - } + if (wasVisible && this->isVisibleDirect()) { + emit this->backerVisibilityChanged(); + this->onExposed(); } } @@ -277,21 +272,24 @@ void ProxyWindowBase::setVisible(bool visible) { void ProxyWindowBase::setVisibleDirect(bool visible) { if (this->deleteOnInvisible()) { + if (visible == this->isVisibleDirect()) return; + if (visible) { - if (visible == this->isVisibleDirect()) return; this->createWindow(); this->polishItems(); this->window->setVisible(true); - this->bBackerVisibility = true; + emit this->backerVisibilityChanged(); } else { - if (this->window != nullptr) this->window->setVisible(false); - this->bBackerVisibility = false; - this->deleteWindow(); + if (this->window != nullptr) { + this->window->setVisible(false); + emit this->backerVisibilityChanged(); + this->deleteWindow(); + } } } else if (this->window != nullptr) { if (visible) this->polishItems(); this->window->setVisible(visible); - this->bBackerVisibility = visible; + emit this->backerVisibilityChanged(); } } diff --git a/src/window/proxywindow.hpp b/src/window/proxywindow.hpp index 86d66f8..025b970 100644 --- a/src/window/proxywindow.hpp +++ b/src/window/proxywindow.hpp @@ -101,10 +101,6 @@ public: virtual void setVisible(bool visible); virtual void setVisibleDirect(bool visible); - [[nodiscard]] QBindable bindableBackerVisibility() const { - return &this->bBackerVisibility; - } - void schedulePolish(); [[nodiscard]] virtual qint32 x() const; @@ -210,13 +206,6 @@ protected: &ProxyWindowBase::implicitHeightChanged ); - Q_OBJECT_BINDABLE_PROPERTY( - ProxyWindowBase, - bool, - bBackerVisibility, - &ProxyWindowBase::backerVisibilityChanged - ); - private: void polishItems(); void updateMask(); diff --git a/src/window/test/popupwindow.cpp b/src/window/test/popupwindow.cpp index f9498d2..1262044 100644 --- a/src/window/test/popupwindow.cpp +++ b/src/window/test/popupwindow.cpp @@ -13,7 +13,7 @@ void TestPopupWindow::initiallyVisible() { // NOLINT auto parent = ProxyWindowBase(); auto popup = ProxyPopupWindow(); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); popup.setVisible(true); parent.reload(); @@ -33,7 +33,7 @@ void TestPopupWindow::reloadReparent() { // NOLINT win2->setVisible(true); parent.setVisible(true); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); popup.setVisible(true); parent.reload(); @@ -43,7 +43,7 @@ void TestPopupWindow::reloadReparent() { // NOLINT auto newParent = ProxyWindowBase(); auto newPopup = ProxyPopupWindow(); - newPopup.anchor()->setWindow(&newParent); + newPopup.setParentWindow(&newParent); newPopup.setVisible(true); auto* oldWindow = popup.backingWindow(); @@ -66,7 +66,7 @@ void TestPopupWindow::reloadUnparent() { // NOLINT auto parent = ProxyWindowBase(); auto popup = ProxyPopupWindow(); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); popup.setVisible(true); parent.reload(); @@ -80,7 +80,8 @@ void TestPopupWindow::reloadUnparent() { // NOLINT newPopup.reload(&popup); QVERIFY(!newPopup.isVisible()); - QVERIFY(!newPopup.backingWindow() || !newPopup.backingWindow()->isVisible()); + QVERIFY(!newPopup.backingWindow()->isVisible()); + QCOMPARE(newPopup.backingWindow()->transientParent(), nullptr); } void TestPopupWindow::invisibleWithoutParent() { // NOLINT @@ -96,11 +97,9 @@ void TestPopupWindow::moveWithParent() { // NOLINT auto parent = ProxyWindowBase(); auto popup = ProxyPopupWindow(); - popup.anchor()->setWindow(&parent); - auto rect = popup.anchor()->rect(); - rect.x = 10; - rect.y = 10; - popup.anchor()->setRect(rect); + popup.setParentWindow(&parent); + popup.setRelativeX(10); + popup.setRelativeY(10); popup.setVisible(true); parent.reload(); @@ -127,7 +126,7 @@ void TestPopupWindow::attachParentLate() { // NOLINT QVERIFY(!popup.isVisible()); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); QVERIFY(popup.isVisible()); QVERIFY(popup.backingWindow()->isVisible()); QCOMPARE(popup.backingWindow()->transientParent(), parent.backingWindow()); @@ -137,7 +136,7 @@ void TestPopupWindow::reparentLate() { // NOLINT auto parent = ProxyWindowBase(); auto popup = ProxyPopupWindow(); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); popup.setVisible(true); parent.reload(); @@ -152,7 +151,7 @@ void TestPopupWindow::reparentLate() { // NOLINT parent2.backingWindow()->setX(10); parent2.backingWindow()->setY(10); - popup.anchor()->setWindow(&parent2); + popup.setParentWindow(&parent2); QVERIFY(popup.isVisible()); QVERIFY(popup.backingWindow()->isVisible()); QCOMPARE(popup.backingWindow()->transientParent(), parent2.backingWindow()); @@ -164,7 +163,7 @@ void TestPopupWindow::xMigrationFix() { // NOLINT auto parent = ProxyWindowBase(); auto popup = ProxyPopupWindow(); - popup.anchor()->setWindow(&parent); + popup.setParentWindow(&parent); popup.setVisible(true); parent.reload();