services: network&wifi only send notification toasts if the adapter has changed state externally

This commit is contained in:
Lemmy
2026-05-03 19:30:08 -04:00
parent 42bf745595
commit 0b0122653e
6 changed files with 104 additions and 33 deletions
+33 -21
View File
@@ -527,13 +527,14 @@ void Application::initServices() {
try {
m_networkService = std::make_unique<NetworkService>(*m_systemBus);
m_prevWirelessEnabledForEvents = m_networkService->state().wirelessEnabled;
m_networkService->setChangeCallback([this, shouldRefreshControlCenter](const NetworkState& state) {
onNetworkStateChangedForEvents(state);
m_bar.refresh();
if (shouldRefreshControlCenter()) {
m_panelManager.refresh();
}
});
m_networkService->setChangeCallback(
[this, shouldRefreshControlCenter](const NetworkState& state, NetworkChangeOrigin origin) {
onNetworkStateChangedForEvents(state, origin);
m_bar.refresh();
if (shouldRefreshControlCenter()) {
m_panelManager.refresh();
}
});
kLog.info("network service active");
} catch (const std::exception& e) {
kLog.warn("network service disabled: {}", e.what());
@@ -558,10 +559,11 @@ void Application::initServices() {
m_panelManager.refresh();
}
};
m_bluetoothService->setStateCallback([this, refreshBluetoothUi](const BluetoothState& state) {
onBluetoothStateChangedForEvents(state);
refreshBluetoothUi();
});
m_bluetoothService->setStateCallback(
[this, refreshBluetoothUi](const BluetoothState& state, BluetoothStateChangeOrigin origin) {
onBluetoothStateChangedForEvents(state, origin);
refreshBluetoothUi();
});
m_bluetoothService->setDevicesCallback(
[refreshBluetoothUi](const std::vector<BluetoothDeviceInfo>& /*devices*/) { refreshBluetoothUi(); });
kLog.info("bluetooth service active");
@@ -1172,40 +1174,50 @@ void Application::onUpowerStateChangedForHooks() {
m_prevUpowerForHooks = next;
}
void Application::onNetworkStateChangedForEvents(const NetworkState& state) {
void Application::onNetworkStateChangedForEvents(const NetworkState& state, NetworkChangeOrigin origin) {
if (!m_prevWirelessEnabledForEvents.has_value()) {
m_prevWirelessEnabledForEvents = state.wirelessEnabled;
return;
}
const bool prev = *m_prevWirelessEnabledForEvents;
if (prev != state.wirelessEnabled) {
if (origin != NetworkChangeOrigin::Noctalia) {
if (state.wirelessEnabled) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-enabled"), "");
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-disabled"), "");
}
}
if (state.wirelessEnabled) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-enabled"), "");
m_hookManager.fire(HookKind::WifiEnabled);
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.network"),
i18n::tr("notifications.internal.wifi-disabled"), "");
m_hookManager.fire(HookKind::WifiDisabled);
}
}
m_prevWirelessEnabledForEvents = state.wirelessEnabled;
}
void Application::onBluetoothStateChangedForEvents(const BluetoothState& state) {
void Application::onBluetoothStateChangedForEvents(const BluetoothState& state, BluetoothStateChangeOrigin origin) {
if (!m_prevBluetoothPoweredForEvents.has_value()) {
m_prevBluetoothPoweredForEvents = state.powered;
return;
}
const bool prev = *m_prevBluetoothPoweredForEvents;
if (prev != state.powered) {
if (origin != BluetoothStateChangeOrigin::Noctalia) {
if (state.powered) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-enabled"), "");
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-disabled"), "");
}
}
if (state.powered) {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-enabled"), "");
m_hookManager.fire(HookKind::BluetoothEnabled);
} else {
m_notificationManager.addInternal(i18n::tr("notifications.internal.bluetooth"),
i18n::tr("notifications.internal.bluetooth-disabled"), "");
m_hookManager.fire(HookKind::BluetoothDisabled);
}
}
+2 -2
View File
@@ -109,8 +109,8 @@ private:
bool runIdleCommand(const std::string& command);
void onIconThemeChanged();
void onUpowerStateChangedForHooks();
void onNetworkStateChangedForEvents(const NetworkState& state);
void onBluetoothStateChangedForEvents(const BluetoothState& state);
void onNetworkStateChangedForEvents(const NetworkState& state, NetworkChangeOrigin origin);
void onBluetoothStateChangedForEvents(const BluetoothState& state, BluetoothStateChangeOrigin origin);
[[nodiscard]] std::vector<PollSource*> currentPollSources();
[[nodiscard]] std::vector<PollSource*> buildPollSources();
+27 -5
View File
@@ -326,12 +326,15 @@ struct BluetoothService::Impl {
BluetoothState next = self.m_state;
readAdapterProps(changed, next);
next.adapterPresent = true;
if (next.powered != self.m_state.powered) {
const bool poweredChanged = next.powered != self.m_state.powered;
BluetoothStateChangeOrigin origin = BluetoothStateChangeOrigin::External;
if (poweredChanged) {
kLog.debug("adapter Powered -> {}", next.powered);
origin = self.consumePoweredChangeOrigin(next.powered);
}
if (next != self.m_state) {
self.m_state = std::move(next);
self.emitState();
self.emitState(origin);
}
return;
}
@@ -442,12 +445,16 @@ void BluetoothService::refresh() {
try {
ManagedObjects objects;
m_impl->root->callMethod("GetManagedObjects").onInterface(k_objectManagerInterface).storeResultsTo(objects);
const BluetoothState previous = m_state;
m_devices.clear();
m_state = BluetoothState{};
m_impl->adapterPath.clear();
m_impl->adapter.reset();
m_impl->seedFromManagedObjects(objects);
emitState();
const BluetoothStateChangeOrigin origin = previous.powered != m_state.powered
? consumePoweredChangeOrigin(m_state.powered)
: BluetoothStateChangeOrigin::External;
emitState(origin);
emitDevices();
} catch (const sdbus::Error& e) {
kLog.debug("GetManagedObjects failed: {}", e.what());
@@ -458,6 +465,9 @@ void BluetoothService::setPowered(bool enabled) {
if (m_impl->adapter == nullptr) {
return;
}
if (enabled != m_state.powered) {
m_pendingLocalPowered = enabled;
}
try {
if (!enabled && m_state.discovering) {
try {
@@ -467,6 +477,9 @@ void BluetoothService::setPowered(bool enabled) {
}
m_impl->adapter->setProperty("Powered").onInterface(k_adapterInterface).toValue(enabled);
} catch (const sdbus::Error& e) {
if (m_pendingLocalPowered == enabled) {
m_pendingLocalPowered.reset();
}
kLog.warn("setPowered failed: {}", e.what());
}
}
@@ -618,9 +631,18 @@ BluetoothDeviceInfo* BluetoothService::findDevice(const std::string& path) {
return it == m_devices.end() ? nullptr : &*it;
}
void BluetoothService::emitState() {
BluetoothStateChangeOrigin BluetoothService::consumePoweredChangeOrigin(bool powered) {
if (!m_pendingLocalPowered.has_value()) {
return BluetoothStateChangeOrigin::External;
}
const bool matchesLocalRequest = *m_pendingLocalPowered == powered;
m_pendingLocalPowered.reset();
return matchesLocalRequest ? BluetoothStateChangeOrigin::Noctalia : BluetoothStateChangeOrigin::External;
}
void BluetoothService::emitState(BluetoothStateChangeOrigin origin) {
if (m_stateCallback) {
m_stateCallback(m_state);
m_stateCallback(m_state, origin);
}
}
+10 -2
View File
@@ -3,6 +3,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
@@ -57,9 +58,14 @@ struct BluetoothState {
bool operator==(const BluetoothState&) const = default;
};
enum class BluetoothStateChangeOrigin : std::uint8_t {
External,
Noctalia,
};
class BluetoothService {
public:
using StateCallback = std::function<void(const BluetoothState&)>;
using StateCallback = std::function<void(const BluetoothState&, BluetoothStateChangeOrigin)>;
using DevicesCallback = std::function<void(const std::vector<BluetoothDeviceInfo>&)>;
explicit BluetoothService(SystemBus& bus);
@@ -93,13 +99,15 @@ private:
friend struct Impl;
BluetoothDeviceInfo* findDevice(const std::string& path);
void emitState();
[[nodiscard]] BluetoothStateChangeOrigin consumePoweredChangeOrigin(bool powered);
void emitState(BluetoothStateChangeOrigin origin = BluetoothStateChangeOrigin::External);
void emitDevices();
std::unique_ptr<Impl> m_impl;
BluetoothState m_state;
std::vector<BluetoothDeviceInfo> m_devices;
std::optional<bool> m_pendingLocalPowered;
StateCallback m_stateCallback;
DevicesCallback m_devicesCallback;
};
+23 -2
View File
@@ -204,9 +204,12 @@ void NetworkService::refresh() {
const bool apsChanged = previousAps != m_accessPoints;
const bool savedChanged = previousSaved != m_savedSsids;
const bool stateChanged = next != m_state;
const bool wirelessEnabledChanged = next.wirelessEnabled != m_state.wirelessEnabled;
const NetworkChangeOrigin origin =
wirelessEnabledChanged ? consumeWirelessEnabledChangeOrigin(next.wirelessEnabled) : NetworkChangeOrigin::External;
m_state = std::move(next);
if ((stateChanged || apsChanged || savedChanged) && m_changeCallback) {
m_changeCallback(m_state);
m_changeCallback(m_state, origin);
}
}
@@ -299,9 +302,15 @@ bool NetworkService::activateAccessPoint(const AccessPointInfo& ap) {
}
void NetworkService::setWirelessEnabled(bool enabled) {
if (enabled != m_state.wirelessEnabled) {
m_pendingLocalWirelessEnabled = enabled;
}
try {
m_nm->setProperty("WirelessEnabled").onInterface(k_nmInterface).toValue(enabled);
} catch (const sdbus::Error& e) {
if (m_pendingLocalWirelessEnabled == enabled) {
m_pendingLocalWirelessEnabled.reset();
}
kLog.warn("WirelessEnabled write failed: {}", e.what());
}
}
@@ -727,12 +736,24 @@ NetworkState NetworkService::readState() {
return next;
}
NetworkChangeOrigin NetworkService::consumeWirelessEnabledChangeOrigin(bool enabled) {
if (!m_pendingLocalWirelessEnabled.has_value()) {
return NetworkChangeOrigin::External;
}
const bool matchesLocalRequest = *m_pendingLocalWirelessEnabled == enabled;
m_pendingLocalWirelessEnabled.reset();
return matchesLocalRequest ? NetworkChangeOrigin::Noctalia : NetworkChangeOrigin::External;
}
void NetworkService::emitChangedIfNeeded(NetworkState next) {
if (next == m_state) {
return;
}
const bool wirelessEnabledChanged = next.wirelessEnabled != m_state.wirelessEnabled;
const NetworkChangeOrigin origin =
wirelessEnabledChanged ? consumeWirelessEnabledChangeOrigin(next.wirelessEnabled) : NetworkChangeOrigin::External;
m_state = std::move(next);
if (m_changeCallback) {
m_changeCallback(m_state);
m_changeCallback(m_state, origin);
}
}
+9 -1
View File
@@ -3,6 +3,7 @@
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
@@ -44,9 +45,14 @@ struct NetworkState {
bool operator==(const NetworkState&) const = default;
};
enum class NetworkChangeOrigin : std::uint8_t {
External,
Noctalia,
};
class NetworkService {
public:
using ChangeCallback = std::function<void(const NetworkState&)>;
using ChangeCallback = std::function<void(const NetworkState&, NetworkChangeOrigin)>;
explicit NetworkService(SystemBus& bus);
~NetworkService();
@@ -93,6 +99,7 @@ private:
void rebindActiveAccessPoint(const std::string& apPath);
void ensureWifiDeviceSubscribed(const std::string& devicePath);
[[nodiscard]] NetworkState readState();
[[nodiscard]] NetworkChangeOrigin consumeWirelessEnabledChangeOrigin(bool enabled);
void emitChangedIfNeeded(NetworkState next);
SystemBus& m_bus;
@@ -109,5 +116,6 @@ private:
std::vector<std::string> m_savedSsids;
bool m_scanning = false;
std::int64_t m_scanBaselineLastScan = 0;
std::optional<bool> m_pendingLocalWirelessEnabled;
ChangeCallback m_changeCallback;
};