Skip to content

Commit bee9b72

Browse files
committed
network: add WifiDeviceMode, update NM wireless logic
- Add WifiDeviceMode and WifiDevice.mode - Add module.md - Reimplement NMWirelessNetwork - Fix mode checking for NMWirelessDevice onConnectionLoaded/onAccessPointLoaded - Move security type to bindings in NMAccessPoint and NMConnectionSettings
1 parent b031b02 commit bee9b72

File tree

17 files changed

+514
-356
lines changed

17 files changed

+514
-356
lines changed

src/network/module.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name = "Quickshell.Network"
2+
description = "Network API"
3+
headers = [
4+
"network.hpp",
5+
"device.hpp",
6+
"wifi.hpp",
7+
]
8+
-----
9+
This modules exposes Network management APIs provided by a supported network backend.
10+
For now, the only backend available is the NetworkManager DBus interface.
11+
Both DBus and NetworkManager must be running to use it.
12+
13+
See the @@Quickshell.Network.Network singleton.

src/network/network.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@ QS_LOGGING_CATEGORY(logNetwork, "quickshell.network", QtWarningMsg);
1717
} // namespace
1818

1919
Network::Network(QObject* parent): QObject(parent) {
20-
// NetworkManager
20+
// Try to create the NetworkManager backend and bind to it.
2121
auto* nm = new NetworkManager(this);
2222
if (nm->isAvailable()) {
2323
QObject::connect(nm, &NetworkManager::deviceAdded, this, &Network::onDeviceAdded);
2424
QObject::connect(nm, &NetworkManager::deviceRemoved, this, &Network::onDeviceRemoved);
25+
QObject::connect(this, &Network::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled);
2526
this->bindableWifiEnabled().setBinding([nm]() { return nm->wifiEnabled(); });
2627
this->bindableWifiHardwareEnabled().setBinding([nm]() { return nm->wifiHardwareEnabled(); });
27-
QObject::connect(this, &Network::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled);
28+
2829
this->mBackend = nm;
2930
this->mBackendType = NetworkBackendType::NetworkManager;
3031
return;
@@ -41,7 +42,7 @@ void Network::onDeviceRemoved(NetworkDevice* dev) { this->mDevices.removeObject(
4142
void Network::setWifiEnabled(bool enabled) {
4243
if (this->bWifiEnabled == enabled) {
4344
const QString state = enabled ? "enabled" : "disabled";
44-
qCCritical(logNetwork) << "Wifi is already software" << state;
45+
qCCritical(logNetwork) << "Wifi is already globally software" << state;
4546
} else {
4647
emit this->requestSetWifiEnabled(enabled);
4748
}

src/network/network.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ class Network: public QObject {
4848
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::Network::Device>*);
4949
/// The backend being used to power the Network service.
5050
Q_PROPERTY(qs::network::NetworkBackendType::Enum backend READ backend CONSTANT);
51-
/// Master switch for the status of the rfkill software block of all wireless devices.
51+
/// Switch for the rfkill software block of all wireless devices.
5252
Q_PROPERTY(bool wifiEnabled READ wifiEnabled WRITE setWifiEnabled NOTIFY wifiEnabledChanged);
53-
/// Master switch for the status of the rfkill hardware block of all wireless devices.
53+
/// Switch for the rfkill hardware block of all wireless devices.
5454
Q_PROPERTY(bool wifiHardwareEnabled READ wifiHardwareEnabled NOTIFY wifiHardwareEnabledChanged BINDABLE bindableWifiHardwareEnabled);
5555
// clang-format on
5656

@@ -90,9 +90,9 @@ class BaseNetwork: public QObject {
9090
QML_ELEMENT;
9191
QML_UNCREATABLE("BaseNetwork can only be aqcuired through network devices");
9292

93-
/// The name of the network
93+
/// The name of the network.
9494
Q_PROPERTY(QString name READ name CONSTANT);
95-
/// True if the network is connected to.
95+
/// True if the network is connected.
9696
Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged BINDABLE bindableConnected);
9797

9898
public:

src/network/nm/accesspoint.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,15 @@ NMAccessPoint::NMAccessPoint(const QString& path, QObject* parent): QObject(pare
2929
);
3030

3131
if (!this->proxy->isValid()) {
32-
qCWarning(logNetworkManager) << "Cannot create DBus interface for AP at" << path;
32+
qCWarning(logNetworkManager) << "Cannot create DBus interface for access point at" << path;
3333
return;
3434
}
3535

3636
QObject::connect(
3737
&this->accessPointProperties,
3838
&DBusPropertyGroup::getAllFinished,
3939
this,
40-
&NMAccessPoint::ready,
40+
&NMAccessPoint::loaded,
4141
Qt::SingleShotConnection
4242
);
4343

src/network/nm/accesspoint.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <qtypes.h>
88

99
#include "../../dbus/properties.hpp"
10+
#include "../wifi.hpp"
1011
#include "enums.hpp"
1112
#include "nm/dbus_nm_accesspoint.h"
1213

@@ -53,27 +54,29 @@ class NMAccessPoint: public QObject {
5354
[[nodiscard]] NM80211ApSecurityFlags::Enum wpaFlags() const { return this->bWpaFlags; };
5455
[[nodiscard]] NM80211ApSecurityFlags::Enum rsnFlags() const { return this->bRsnFlags; };
5556
[[nodiscard]] NM80211Mode::Enum mode() const { return this->bMode; };
57+
[[nodiscard]] QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; };
58+
[[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; };
5659

5760
signals:
58-
void ready();
61+
void loaded();
5962
void disappeared();
6063
void ssidChanged(const QByteArray& ssid);
6164
void signalStrengthChanged(quint8 signal);
6265
void flagsChanged(NM80211ApFlags::Enum flags);
6366
void wpaFlagsChanged(NM80211ApSecurityFlags::Enum wpaFlags);
6467
void rsnFlagsChanged(NM80211ApSecurityFlags::Enum rsnFlags);
6568
void modeChanged(NM80211Mode::Enum mode);
69+
void securityChanged(WifiSecurityType::Enum security);
6670

6771
private:
68-
bool mActive = false;
69-
7072
// clang-format off
7173
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, QByteArray, bSsid, &NMAccessPoint::ssidChanged);
7274
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, quint8, bSignalStrength, &NMAccessPoint::signalStrengthChanged);
7375
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApFlags::Enum, bFlags, &NMAccessPoint::flagsChanged);
7476
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApSecurityFlags::Enum, bWpaFlags, &NMAccessPoint::wpaFlagsChanged);
7577
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211ApSecurityFlags::Enum, bRsnFlags, &NMAccessPoint::rsnFlagsChanged);
7678
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, NM80211Mode::Enum, bMode, &NMAccessPoint::modeChanged);
79+
Q_OBJECT_BINDABLE_PROPERTY(NMAccessPoint, WifiSecurityType::Enum, bSecurity, &NMAccessPoint::securityChanged);
7780

7881
QS_DBUS_BINDABLE_PROPERTY_GROUP(NMAccessPointAdapter, accessPointProperties);
7982
QS_DBUS_PROPERTY_BINDING(NMAccessPoint, pSsid, bSsid, accessPointProperties, "Ssid");

src/network/nm/backend.cpp

Lines changed: 87 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@
1010
#include <qloggingcategory.h>
1111
#include <qobject.h>
1212
#include <qtmetamacros.h>
13-
#include <qxmlstream.h>
1413

1514
#include "../../core/logcat.hpp"
1615
#include "../../dbus/properties.hpp"
1716
#include "../device.hpp"
1817
#include "../network.hpp"
1918
#include "../wifi.hpp"
2019
#include "dbus_types.hpp"
20+
#include "device.hpp"
21+
#include "enums.hpp"
2122
#include "nm/dbus_nm_backend.h"
2223
#include "wireless.hpp"
2324

@@ -86,101 +87,132 @@ void NetworkManager::registerDevices() {
8687
}
8788

8889
void NetworkManager::registerDevice(const QString& path) {
89-
if (this->mDeviceHash.contains(path)) {
90+
if (this->mDevices.contains(path)) {
9091
qCDebug(logNetworkManager) << "Skipping duplicate registration of device" << path;
9192
return;
9293
}
9394

94-
// Introspect to decide the device variant. (For now, only Wireless)
95-
auto* introspection = new QDBusInterface(
95+
// Create a temporary interface to decide the device type which won't change.
96+
auto* temp = new QDBusInterface(
9697
"org.freedesktop.NetworkManager",
9798
path,
98-
"org.freedesktop.DBus.Introspectable",
99+
"org.freedesktop.DBus.Properties",
99100
QDBusConnection::systemBus(),
100101
this
101102
);
102103

103-
auto pending = introspection->asyncCall("Introspect");
104+
auto pending = temp->asyncCall("Get", "org.freedesktop.NetworkManager.Device", "DeviceType");
104105
auto* call = new QDBusPendingCallWatcher(pending, this);
105106

106-
auto responseCallback = [this, path, introspection](QDBusPendingCallWatcher* call) {
107-
const QDBusPendingReply<QString> reply = *call;
108-
107+
auto responseCallback = [this, path, temp](QDBusPendingCallWatcher* call) {
108+
const QDBusPendingReply<QVariant> reply = *call;
109109
if (reply.isError()) {
110-
qCWarning(logNetworkManager) << "Failed to introspect device: " << reply.error().message();
110+
qCWarning(logNetworkManager) << "Failed to get device type: " << reply.error().message();
111111
} else {
112-
QXmlStreamReader xml(reply.value());
113-
114-
while (!xml.atEnd() && !xml.hasError()) {
115-
xml.readNext();
116-
117-
if (xml.isStartElement() && xml.name() == u"interface") {
118-
const QString name = xml.attributes().value("name").toString();
119-
if (name.startsWith("org.freedesktop.NetworkManager.Device.Wireless")) {
120-
this->registerWifiDevice(path);
121-
break;
122-
}
123-
}
112+
auto type = static_cast<qs::network::NMDeviceType::Enum>(reply.value().toInt());
113+
NMDevice* dev = nullptr;
114+
115+
switch (type) {
116+
case NMDeviceType::Wifi: dev = new NMWirelessDevice(path); break;
117+
default: return;
118+
}
119+
120+
if (!dev->isValid()) {
121+
qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path;
122+
delete dev;
123+
return;
124124
}
125+
126+
// Only register a frontend device while it's managed by NM.
127+
auto onManagedChanged = [this, dev, type](bool managed) {
128+
managed ? this->registerFrontendDevice(type, dev) : this->removeFrontendDevice(dev);
129+
};
130+
// clang-format off
131+
QObject::connect(dev, &NMDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection);
132+
QObject::connect(dev, &NMDevice::activateConnection, this, &NetworkManager::activateConnection);
133+
QObject::connect(dev, &NMDevice::managedChanged, this, onManagedChanged);
134+
// clang-format on
135+
136+
this->mDevices.insert(path, dev);
137+
if (dev->managed()) this->registerFrontendDevice(type, dev);
125138
}
126139
delete call;
127-
delete introspection;
140+
delete temp;
128141
};
129142

130143
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
131144
}
132145

133-
void NetworkManager::registerWifiDevice(const QString& path) {
134-
auto* wireless = new NMWirelessDevice(path);
135-
if (!wireless->isWirelessValid() || !wireless->isDeviceValid()) {
136-
qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path;
137-
delete wireless;
138-
return;
146+
void NetworkManager::registerFrontendDevice(NMDeviceType::Enum type, NMDevice* dev) {
147+
NetworkDevice* frontendDev = nullptr;
148+
switch (type) {
149+
case NMDeviceType::Wifi: {
150+
auto* frontendWifiDev = new WifiDevice(dev);
151+
auto* wifiDev = qobject_cast<NMWirelessDevice*>(dev);
152+
// Bind WifiDevice-specific properties
153+
auto translateMode = [wifiDev]() {
154+
switch (wifiDev->mode()) {
155+
case NM80211Mode::Unknown: return WifiDeviceMode::Unknown;
156+
case NM80211Mode::Adhoc: return WifiDeviceMode::AdHoc;
157+
case NM80211Mode::Infra: return WifiDeviceMode::Station;
158+
case NM80211Mode::Ap: return WifiDeviceMode::AccessPoint;
159+
case NM80211Mode::Mesh: return WifiDeviceMode::Mesh;
160+
}
161+
};
162+
// clang-format off
163+
frontendWifiDev->bindableMode().setBinding(translateMode);
164+
wifiDev->bindableScanning().setBinding([frontendWifiDev]() { return frontendWifiDev->wifiScanner()->enabled(); });
165+
QObject::connect(wifiDev, &NMWirelessDevice::networkAdded, frontendWifiDev, &WifiDevice::networkAdded);
166+
QObject::connect(wifiDev, &NMWirelessDevice::networkRemoved, frontendWifiDev, &WifiDevice::networkRemoved);
167+
// clang-format on
168+
frontendDev = frontendWifiDev;
169+
break;
170+
}
171+
default: return;
139172
}
140173

141-
auto* device = new WifiDevice(this);
142-
auto* scanner = device->wifiScanner();
143-
wireless->setParent(device);
144-
this->mDeviceHash.insert(path, device);
145-
146-
device->bindableName().setBinding([wireless]() { return wireless->interface(); });
147-
device->bindableAddress().setBinding([wireless]() { return wireless->hwAddress(); });
148-
device->bindableNmState().setBinding([wireless]() { return wireless->state(); });
149-
device->bindableState().setBinding([wireless]() {
150-
switch (wireless->state()) {
174+
// Bind generic NetworkDevice properties
175+
auto translateState = [dev]() {
176+
switch (dev->state()) {
151177
case 0 ... 20: return DeviceConnectionState::Unknown;
152178
case 30: return DeviceConnectionState::Disconnected;
153179
case 40 ... 90: return DeviceConnectionState::Connecting;
154180
case 100: return DeviceConnectionState::Connected;
155181
case 110 ... 120: return DeviceConnectionState::Disconnecting;
156182
}
157-
});
158-
// clang-format off
159-
QObject::connect(wireless, &NMWirelessDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection);
160-
QObject::connect(wireless, &NMWirelessDevice::activateConnection, this, &NetworkManager::activateConnection);
161-
QObject::connect(wireless, &NMWirelessDevice::networkAdded, device, &WifiDevice::networkAdded);
162-
QObject::connect(wireless, &NMWirelessDevice::networkRemoved, device, &WifiDevice::networkRemoved);
163-
QObject::connect(scanner, &WifiScanner::requestEnabled, wireless, &NMWirelessDevice::handleScanner);
164-
QObject::connect(device, &WifiDevice::requestDisconnect, wireless, &NMWirelessDevice::disconnect);
165-
// clang-format on
183+
};
184+
frontendDev->bindableName().setBinding([dev]() { return dev->interface(); });
185+
frontendDev->bindableAddress().setBinding([dev]() { return dev->hwAddress(); });
186+
frontendDev->bindableNmState().setBinding([dev]() { return dev->state(); });
187+
frontendDev->bindableState().setBinding(translateState);
188+
QObject::connect(frontendDev, &WifiDevice::requestDisconnect, dev, &NMDevice::disconnect);
189+
190+
this->mFrontendDevices.insert(dev->path(), frontendDev);
191+
emit this->deviceAdded(frontendDev);
192+
}
166193

167-
emit this->deviceAdded(device);
194+
void NetworkManager::removeFrontendDevice(NMDevice* dev) {
195+
auto* frontendDev = this->mFrontendDevices.take(dev->path());
196+
if (frontendDev) {
197+
emit this->deviceRemoved(frontendDev);
198+
delete frontendDev;
199+
}
168200
}
169201

170202
void NetworkManager::onDevicePathAdded(const QDBusObjectPath& path) {
171203
this->registerDevice(path.path());
172204
}
173205

174206
void NetworkManager::onDevicePathRemoved(const QDBusObjectPath& path) {
175-
auto iter = this->mDeviceHash.find(path.path());
176-
if (iter == this->mDeviceHash.end()) {
207+
auto iter = this->mDevices.find(path.path());
208+
if (iter == this->mDevices.end()) {
177209
qCWarning(logNetworkManager) << "Sent removal signal for" << path.path()
178210
<< "which is not registered.";
179211
} else {
180-
auto* device = iter.value();
181-
this->mDeviceHash.erase(iter);
182-
emit this->deviceRemoved(device);
183-
delete device;
212+
auto* dev = iter.value();
213+
this->removeFrontendDevice(dev);
214+
this->mDevices.erase(iter);
215+
delete dev;
184216
}
185217
}
186218

src/network/nm/backend.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include "../../dbus/properties.hpp"
1111
#include "../network.hpp"
12-
#include "../wifi.hpp"
12+
#include "device.hpp"
1313
#include "nm/dbus_nm_backend.h"
1414

1515
namespace qs::network {
@@ -47,9 +47,11 @@ private slots:
4747
void init();
4848
void registerDevices();
4949
void registerDevice(const QString& path);
50-
void registerWifiDevice(const QString& path);
50+
void registerFrontendDevice(NMDeviceType::Enum type, NMDevice* dev);
51+
void removeFrontendDevice(NMDevice* dev);
5152

52-
QHash<QString, NetworkDevice*> mDeviceHash;
53+
QHash<QString, NMDevice*> mDevices;
54+
QHash<QString, NetworkDevice*> mFrontendDevices;
5355

5456
// clang-format off
5557
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiEnabled, &NetworkManager::wifiEnabledChanged);

src/network/nm/connection.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "enums.hpp"
2020
#include "nm/dbus_nm_active_connection.h"
2121
#include "nm/dbus_nm_connection_settings.h"
22+
#include "nm/utils.hpp"
2223

2324
namespace qs::network {
2425
using namespace qs::dbus;
@@ -48,6 +49,7 @@ NMConnectionSettings::NMConnectionSettings(const QString& path, QObject* parent)
4849
this,
4950
&NMConnectionSettings::updateSettings
5051
);
52+
this->bSecurity.setBinding([this]() { return securityFromConnectionSettings(this->bSettings); });
5153

5254
this->connectionSettingsProperties.setInterface(this->proxy);
5355
this->connectionSettingsProperties.updateAllViaGetAll();
@@ -69,7 +71,10 @@ void NMConnectionSettings::updateSettings() {
6971
this->bSettings = reply.value();
7072
}
7173

72-
emit this->ready();
74+
if (!this->mLoaded) {
75+
emit this->loaded();
76+
this->mLoaded = true;
77+
}
7378
delete call;
7479
};
7580

@@ -96,7 +101,7 @@ NMActiveConnection::NMActiveConnection(const QString& path, QObject* parent): QO
96101
}
97102

98103
// clang-format off
99-
QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::ready, Qt::SingleShotConnection);
104+
QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::loaded, Qt::SingleShotConnection);
100105
QObject::connect(this->proxy, &DBusNMActiveConnectionProxy::StateChanged, this, &NMActiveConnection::onStateChanged);
101106
// clang-format on
102107

0 commit comments

Comments
 (0)