Skip to content

network: add NMSettings and NMConnectionContext#535

Open
cpwrs wants to merge 7 commits intoquickshell-mirror:masterfrom
cpwrs:nm-connection
Open

network: add NMSettings and NMConnectionContext#535
cpwrs wants to merge 7 commits intoquickshell-mirror:masterfrom
cpwrs:nm-connection

Conversation

@cpwrs
Copy link
Contributor

@cpwrs cpwrs commented Feb 2, 2026

This exposes the connection settings from NetworkManager. It also exposes a creatable NMConnectionContext object that emits signals about connection attempts and tracks their respective connection settings.

Additions:

  • NMConnectionContext
    • Emits all the signals I could find that would be relevant to a wifi connections lifecycle
    • Keeps a reference to the settings relevant to the signals emitted
  • NMSettings class accessible via Network.nmSettings.
  • NMDevice.nmStateReason
  • Network.connectWithSettings() for the multi-setting network use case
  • Reference types where possible in doc comments

Fixes:

  • Unmanaged devices are now included in the model, and NetworkDevice.managed is a writable prop
  • Moved WifiNetwork.{connect, disconnect, forget}() to its parent class Network
  • securityFromConnectionSettings correctly handles Unknown, Open, and Owe

@outfoxxed
Copy link
Member

noticing you formatted it, is this ready now?

@cpwrs
Copy link
Contributor Author

cpwrs commented Feb 4, 2026

noticing you formatted it, is this ready now?

Not yet, gotta add back some password stuff. I have time to work on it today

@cpwrs cpwrs force-pushed the nm-connection branch 2 times, most recently from e90eae3 to db97686 Compare February 10, 2026 05:17
@cpwrs cpwrs force-pushed the nm-connection branch 2 times, most recently from dda9fcc to d982e7f Compare February 17, 2026 05:41
@cpwrs cpwrs changed the title network: add NMConnection and NMConnectionContext network: add NMSettings and NMConnectionContext Feb 17, 2026
Comment on lines +93 to +125
void NMConnectionContext::setNetwork(Network* network) {
if (this->bNetwork == network) return;
if (this->bNetwork) disconnect(this->bNetwork, nullptr, this, nullptr);
this->bNetwork = network;
if (!network) return;

QObject::connect(network, &Network::activeSettingsChanged, this, [network, this]() {
if (network->activeSettings()) {
if (this->bSettings) disconnect(this->bSettings, nullptr, this, nullptr);
this->bSettings = network->activeSettings();
QObject::connect(this->bSettings, &NMSettings::destroyed, this, [this]() {
this->bSettings = nullptr;
});
};
});
QObject::connect(network, &Network::stateChanged, this, [network, this]() {
if (network->state() == NetworkState::Connected) emit this->activated();
if (network->state() == NetworkState::Connecting) emit this->activating();
});
QObject::connect(network, &Network::nmStateReasonChanged, this, [network, this]() {
// "Device disconnected" isn't useful, so we defer to the last device fail reason.
if (network->nmStateReason() == NMNetworkStateReason::DeviceDisconnected) {
auto failReason = network->nmDeviceFailReason();
if (failReason == NMDeviceStateReason::NoSecrets) emit this->noSecrets();
if (failReason == NMDeviceStateReason::SupplicantDisconnect)
emit this->supplicantDisconnect();
if (failReason == NMDeviceStateReason::SupplicantFailed) emit this->supplicantFailed();
if (failReason == NMDeviceStateReason::SupplicantTimeout) emit this->supplicantTimeout();
};
});

connect(network, &Network::destroyed, this, [this]() { this->bNetwork = nullptr; });
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I included a really basic set, but it could include signals for anything from
https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceStateReason
or
https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason

The latter unfortunately only emits for VPN connections, for wifi conns its always DeviceDisconnected.

Sadly there's no "auth failed", that falls under NoSecrets

/// A network to provide connection context for or `null`.
Q_PROPERTY(Network* network READ network WRITE setNetwork NOTIFY networkChanged)
/// The last connection settings that context signals were emitted for or `null`.
Q_PROPERTY(NMSettings* settings READ default NOTIFY settingsChanged BINDABLE bindableSettings)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this what you were thinking for a "callback" to the specific settings?

@cpwrs cpwrs marked this pull request as ready for review February 17, 2026 06:05
@cpwrs
Copy link
Contributor Author

cpwrs commented Feb 17, 2026

I couldn't find a useful way to expose the settings as a big QMap (or multiple smaller maps), so it's empty besides the psk setter. Adding invokables for the popular eap methods is easy enough but I wasn't able to test them.

As far as connection context, I was disappointed to learn that only VPN connections hit the more descriptive state reasons in https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason.
I was also not sure how the context should be implemented in QML. Let me know how I can clean this up!

@cpwrs cpwrs force-pushed the nm-connection branch 2 times, most recently from 23cf03a to 8717373 Compare February 23, 2026 03:26
@cpwrs cpwrs marked this pull request as draft February 23, 2026 22:26
@cpwrs cpwrs force-pushed the nm-connection branch 5 times, most recently from 3fe56a4 to e20267d Compare February 27, 2026 21:35
@outfoxxed
Copy link
Member

outfoxxed commented Mar 3, 2026

Just checking in on this because it'd be nice to have in 0.3 which I want to cut this month. I should be around to review on more sane timelines than usual.

@outfoxxed outfoxxed force-pushed the master branch 5 times, most recently from ca90821 to 3cf65af Compare March 17, 2026 17:10
Comment on lines +26 to +53
struct NMIPv6Address {
QByteArray address;
quint32 prefix = 0;
QByteArray gateway;
};

const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Address& addr);
const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Address& addr);

const QDBusArgument&
operator>>(const QDBusArgument& argument, QList<qs::network::NMIPv6Address>& list);
const QDBusArgument&
operator<<(QDBusArgument& argument, const QList<qs::network::NMIPv6Address>& list);

struct NMIPv6Route {
QByteArray destination;
quint32 prefix = 0;
QByteArray nexthop;
quint32 metric = 0;
};

const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Route& route);
const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Route& route);

const QDBusArgument&
operator>>(const QDBusArgument& argument, QList<qs::network::NMIPv6Route>& list);
const QDBusArgument&
operator<<(QDBusArgument& argument, const QList<qs::network::NMIPv6Route>& list);
Copy link
Contributor Author

@cpwrs cpwrs Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These struct settings are deprecated on stable but are still initialized by NM by default

Comment on lines +34 to +44
/// Update the connection with new settings and save the connection to disk.
/// Only changed fields need to be included.
///
/// > [!NOTE] Secrets may be part of the update request,
/// > and will be either stored in persistent storage or sent to a Secret Agent for storage,
/// > depending on the flags associated with each secret.
Q_INVOKABLE void write(const QVariantMap& settings);
/// Get the settings map describing this network configuration.
///
/// > [!NOTE] This will never include any secrets required for connection to the network, as those are often protected.
Q_INVOKABLE QVariantMap read();
Copy link
Contributor Author

@cpwrs cpwrs Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can just be a Q_PROPERTY now if that's preferred. I can throw in read only props for id/uuid too for convenience.

write() is nice because you only have to give it the fields you want changed. Right now this provides setting = null to remove/unset a setting.

@cpwrs cpwrs marked this pull request as ready for review March 19, 2026 23:59
Comment on lines +46 to +53
const QDBusArgument& operator>>(const QDBusArgument& argument, QList<QVariantMap>& list);
const QDBusArgument& operator<<(QDBusArgument& argument, const QList<QVariantMap>& list);
const QDBusArgument& operator>>(const QDBusArgument& argument, QList<quint32>& list);
const QDBusArgument& operator<<(QDBusArgument& argument, const QList<quint32>& list);
const QDBusArgument& operator>>(const QDBusArgument& argument, QList<QList<quint32>>& list);
const QDBusArgument& operator<<(QDBusArgument& argument, const QList<QList<quint32>>& list);
const QDBusArgument& operator>>(const QDBusArgument& argument, QList<QByteArray>& list);
const QDBusArgument& operator<<(QDBusArgument& argument, const QList<QByteArray>& list);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't type alias these because more than one setting uses these signatures and this would be the correct operator for them in any context.

I can give them some general aliases and bring them into network scope if you prefer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants