From 05e00830308a2de11ed478c9ba25e7acb8f480f5 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sat, 10 Feb 2024 17:12:07 +0100 Subject: [PATCH 01/31] feat: add networkmanager module --- src/bar.rs | 1 + src/config/mod.rs | 2 + src/modules/mod.rs | 1 + src/modules/networkmanager.rs | 142 ++++++++++++++++++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 src/modules/networkmanager.rs diff --git a/src/bar.rs b/src/bar.rs index d15bd73..242e1a4 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -386,6 +386,7 @@ fn add_modules( ModuleConfig::Launcher(mut module) => add_module!(module, id), #[cfg(feature = "music")] ModuleConfig::Music(mut module) => add_module!(module, id), + ModuleConfig::Networkmanager(mut module) => add_module!(module, id), ModuleConfig::Script(mut module) => add_module!(module, id), #[cfg(feature = "sys_info")] ModuleConfig::SysInfo(mut module) => add_module!(module, id), diff --git a/src/config/mod.rs b/src/config/mod.rs index bb0d61f..34bd1b8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -14,6 +14,7 @@ use crate::modules::label::LabelModule; use crate::modules::launcher::LauncherModule; #[cfg(feature = "music")] use crate::modules::music::MusicModule; +use crate::modules::networkmanager::NetworkmanagerModule; use crate::modules::script::ScriptModule; #[cfg(feature = "sys_info")] use crate::modules::sysinfo::SysInfoModule; @@ -45,6 +46,7 @@ pub enum ModuleConfig { Launcher(Box), #[cfg(feature = "music")] Music(Box), + Networkmanager(Box), Script(Box), #[cfg(feature = "sys_info")] SysInfo(Box), diff --git a/src/modules/mod.rs b/src/modules/mod.rs index a293d0f..b3b6762 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -35,6 +35,7 @@ pub mod label; pub mod launcher; #[cfg(feature = "music")] pub mod music; +pub mod networkmanager; pub mod script; #[cfg(feature = "sys_info")] pub mod sysinfo; diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs new file mode 100644 index 0000000..ea026a1 --- /dev/null +++ b/src/modules/networkmanager.rs @@ -0,0 +1,142 @@ +use color_eyre::Result; +use futures_lite::StreamExt; +use gtk::prelude::*; +use gtk::{Image, Orientation}; +use serde::Deserialize; +use tokio::sync::mpsc::Receiver; +use zbus::fdo::PropertiesProxy; +use zbus::names::InterfaceName; +use zbus::zvariant::ObjectPath; + +use crate::config::CommonConfig; +use crate::image::ImageProvider; +use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; +use crate::{glib_recv, send_async, spawn}; + +// TODO: Add icon size option +#[derive(Debug, Deserialize, Clone)] +pub struct NetworkmanagerModule { + #[serde(flatten)] + pub common: Option, +} + +#[derive(Clone, Debug)] +pub enum NetworkmanagerState { + Wireless, + WirelessDisconnected, + Wired, + Offline, +} + +impl Module for NetworkmanagerModule { + type SendMessage = NetworkmanagerState; + type ReceiveMessage = (); + + fn name() -> &'static str { + "networkmanager" + } + + fn spawn_controller( + &self, + _: &ModuleInfo, + context: &WidgetContext, + _: Receiver<()>, + ) -> Result<()> { + let tx = context.tx.clone(); + + spawn(async move { + // TODO: Maybe move this into a `client` à la `upower`? + let nm_proxy = { + let dbus = zbus::Connection::system().await?; + PropertiesProxy::builder(&dbus) + .destination("org.freedesktop.NetworkManager")? + .path("/org/freedesktop/NetworkManager")? + .build() + .await? + }; + let device_interface_name = + InterfaceName::from_static_str("org.freedesktop.NetworkManager")?; + + let state = get_network_state(&nm_proxy, &device_interface_name).await?; + send_async!(tx, ModuleUpdateEvent::Update(state)); + + let mut prop_changed_stream = nm_proxy.receive_properties_changed().await?; + while let Some(signal) = prop_changed_stream.next().await { + let args = signal.args()?; + if args.interface_name != device_interface_name { + continue; + } + + let state = get_network_state(&nm_proxy, &device_interface_name).await?; + send_async!(tx, ModuleUpdateEvent::Update(state)); + } + + Result::<()>::Ok(()) + }); + + Ok(()) + } + + fn into_widget( + self, + context: WidgetContext, + info: &ModuleInfo, + ) -> Result> { + let container = gtk::Box::new(Orientation::Horizontal, 0); + let icon = Image::new(); + container.add(&icon); + + let icon_theme = info.icon_theme.clone(); + + let initial_icon_name = "icon:content-loading-symbolic"; + ImageProvider::parse(initial_icon_name, &icon_theme, false, 18) + .map(|provider| provider.load_into_image(icon.clone())); + + let rx = context.subscribe(); + glib_recv!(rx, state => { + let icon_name = match state { + NetworkmanagerState::Wireless => "network-wireless-symbolic", + NetworkmanagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", + NetworkmanagerState::Wired => "network-wired-symbolic", + NetworkmanagerState::Offline => "network-wireless-disabled-symbolic", + }; + ImageProvider::parse(icon_name, &icon_theme, false, 18) + .map(|provider| provider.load_into_image(icon.clone())); + }); + + Ok(ModuleParts::new(container, None)) + } +} + +async fn get_network_state( + nm_proxy: &PropertiesProxy<'_>, + device_interface_name: &InterfaceName<'_>, +) -> Result { + let properties = nm_proxy.get_all(device_interface_name.clone()).await?; + + let primary_connection_path = properties["PrimaryConnection"] + .downcast_ref::() + .unwrap(); + + if primary_connection_path != "/" { + let primary_connection_type = properties["PrimaryConnectionType"] + .downcast_ref::() + .unwrap() + .to_string(); + + match primary_connection_type.as_str() { + "802-11-wireless" => Ok(NetworkmanagerState::Wireless), + "802-3-ethernet" => Ok(NetworkmanagerState::Wired), + _ => panic!("Unknown primary connection type"), + } + } else { + let wireless_enabled = *properties["WirelessEnabled"] + .downcast_ref::() + .unwrap(); + if wireless_enabled { + Ok(NetworkmanagerState::WirelessDisconnected) + } else { + Ok(NetworkmanagerState::Offline) + } + } +} From 33d367cdf45b4214671ed8c3e16da01f13391326 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 15 Feb 2024 15:18:18 +0100 Subject: [PATCH 02/31] feat: networkmanager module is now an optional Cargo feature --- Cargo.toml | 9 ++++++--- src/modules/mod.rs | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2a49691..b9e170f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ default = [ "ipc", "launcher", "music+all", + "networkmanager", "sys_info", "tray", "upower", @@ -56,6 +57,8 @@ music = ["regex"] "music+mpris" = ["music", "mpris"] "music+mpd" = ["music", "mpd-utils"] +networkmanager = ["futures-lite", "zbus"] + sys_info = ["sysinfo", "regex"] tray = ["system-tray"] @@ -129,8 +132,6 @@ system-tray = { version = "0.1.5", optional = true } # upower upower_dbus = { version = "0.3.2", optional = true } -futures-lite = { version = "2.2.0", optional = true } -zbus = { version = "3.15.0", optional = true } # workspaces swayipc-async = { version = "2.0.1", optional = true } @@ -140,4 +141,6 @@ futures-util = { version = "0.3.30", optional = true } # shared regex = { version = "1.10.3", default-features = false, features = [ "std", -], optional = true } # music, sys_info \ No newline at end of file +], optional = true } # music, sys_info +futures-lite = { version = "2.2.0", optional = true } # networkmanager, upower +zbus = { version = "3.15.0", optional = true } # networkmanager, upower \ No newline at end of file diff --git a/src/modules/mod.rs b/src/modules/mod.rs index b3b6762..0a9ac9b 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -35,6 +35,7 @@ pub mod label; pub mod launcher; #[cfg(feature = "music")] pub mod music; +#[cfg(feature = "networkmanager")] pub mod networkmanager; pub mod script; #[cfg(feature = "sys_info")] From b92c8d65ce800585a20d0d693921be11ce644907 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 15 Feb 2024 15:20:39 +0100 Subject: [PATCH 03/31] feat: add icon size config and cellular support to networkmanager module --- src/modules/networkmanager.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index ea026a1..142b317 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -9,6 +9,7 @@ use zbus::names::InterfaceName; use zbus::zvariant::ObjectPath; use crate::config::CommonConfig; +use crate::gtk_helpers::IronbarGtkExt; use crate::image::ImageProvider; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::{glib_recv, send_async, spawn}; @@ -16,16 +17,25 @@ use crate::{glib_recv, send_async, spawn}; // TODO: Add icon size option #[derive(Debug, Deserialize, Clone)] pub struct NetworkmanagerModule { + #[serde(default = "default_icon_size")] + icon_size: i32, + #[serde(flatten)] pub common: Option, } +const fn default_icon_size() -> i32 { + 24 +} + #[derive(Clone, Debug)] pub enum NetworkmanagerState { + Cellular, + Offline, + Unknown, + Wired, Wireless, WirelessDisconnected, - Wired, - Offline, } impl Module for NetworkmanagerModule { @@ -84,23 +94,26 @@ impl Module for NetworkmanagerModule { ) -> Result> { let container = gtk::Box::new(Orientation::Horizontal, 0); let icon = Image::new(); + icon.add_class("icon"); container.add(&icon); let icon_theme = info.icon_theme.clone(); let initial_icon_name = "icon:content-loading-symbolic"; - ImageProvider::parse(initial_icon_name, &icon_theme, false, 18) + ImageProvider::parse(initial_icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); let rx = context.subscribe(); glib_recv!(rx, state => { let icon_name = match state { + NetworkmanagerState::Cellular => "network-cellular-symbolic", + NetworkmanagerState::Offline => "network-wireless-disabled-symbolic", + NetworkmanagerState::Unknown => "dialog-question-symbolic", + NetworkmanagerState::Wired => "network-wired-symbolic", NetworkmanagerState::Wireless => "network-wireless-symbolic", NetworkmanagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", - NetworkmanagerState::Wired => "network-wired-symbolic", - NetworkmanagerState::Offline => "network-wireless-disabled-symbolic", }; - ImageProvider::parse(icon_name, &icon_theme, false, 18) + ImageProvider::parse(icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); }); @@ -125,9 +138,17 @@ async fn get_network_state( .to_string(); match primary_connection_type.as_str() { + "802-11-olpc-mesh" => Ok(NetworkmanagerState::Wireless), "802-11-wireless" => Ok(NetworkmanagerState::Wireless), "802-3-ethernet" => Ok(NetworkmanagerState::Wired), - _ => panic!("Unknown primary connection type"), + "adsl" => Ok(NetworkmanagerState::Wired), + "cdma" => Ok(NetworkmanagerState::Cellular), + "gsm" => Ok(NetworkmanagerState::Cellular), + "pppoe" => Ok(NetworkmanagerState::Wired), + "wifi-p2p" => Ok(NetworkmanagerState::Wireless), + "wimax" => Ok(NetworkmanagerState::Cellular), + "wpan" => Ok(NetworkmanagerState::Wireless), + _ => Ok(NetworkmanagerState::Unknown), } } else { let wireless_enabled = *properties["WirelessEnabled"] From b96d696a74ad8145f0dee6cf3e2a11fc86ce9f27 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 15 Feb 2024 15:21:58 +0100 Subject: [PATCH 04/31] docs: add networkmanager module documentation --- docs/modules/Networkmanager.md | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/modules/Networkmanager.md diff --git a/docs/modules/Networkmanager.md b/docs/modules/Networkmanager.md new file mode 100644 index 0000000..1c09ad8 --- /dev/null +++ b/docs/modules/Networkmanager.md @@ -0,0 +1,72 @@ +Displays network connectivity information. Requires NetworkManager. + +## Configuration + +> Type: `networkmanager` + +| Name | Type | Default | Description | +|-------------|-----------|---------|-------------------------| +| `icon_size` | `integer` | `24` | Size to render icon at. | + +
+JSON + +```json +{ + "end": [ + { + "type": "networkmanager", + "icon_size": 32 + } + ] +} +``` + +
+ +
+TOML + +```toml +[[end]] +type = "networkmanager" +icon_size = 32 +``` + +
+ +
+YAML + +```yaml +end: + - type: "networkmanager" + icon_size: 32 +``` + +
+ +
+Corn + +```corn +{ + end = [ + { + type = "networkmanager" + icon_size = 32 + } + ] +} +``` + +
+ +## Styling + +| Selector | Description | +|------------------------|----------------------------------| +| `.networkmanager` | NetworkManager widget container. | +| `.networkmanger .icon` | NetworkManager widget icon. | + +For more information on styling, please see the [styling guide](styling-guide). From 4df480522828061b74de1d3e952ed56f8807ffbb Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 12 Mar 2024 13:07:06 +0100 Subject: [PATCH 05/31] docs: remove todo for icon size config --- src/modules/networkmanager.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 142b317..7b59f18 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -14,7 +14,6 @@ use crate::image::ImageProvider; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; use crate::{glib_recv, send_async, spawn}; -// TODO: Add icon size option #[derive(Debug, Deserialize, Clone)] pub struct NetworkmanagerModule { #[serde(default = "default_icon_size")] From cdc196dc6fc4a5922581d5c91e2be49777eaada5 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 12 Mar 2024 13:55:26 +0100 Subject: [PATCH 06/31] fix: missing cfg attribute for networkmanager module in bar loading --- src/bar.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bar.rs b/src/bar.rs index 242e1a4..0b738a0 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -386,6 +386,7 @@ fn add_modules( ModuleConfig::Launcher(mut module) => add_module!(module, id), #[cfg(feature = "music")] ModuleConfig::Music(mut module) => add_module!(module, id), + #[cfg(feature = "networkmanager")] ModuleConfig::Networkmanager(mut module) => add_module!(module, id), ModuleConfig::Script(mut module) => add_module!(module, id), #[cfg(feature = "sys_info")] From 85db13d2ef3e91dc2c0b124942c25db19bf26252 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 12 Mar 2024 14:24:34 +0100 Subject: [PATCH 07/31] fix: yet another missing cfg attribute for networkmanager module --- src/config/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 34bd1b8..b8c51ed 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -14,6 +14,7 @@ use crate::modules::label::LabelModule; use crate::modules::launcher::LauncherModule; #[cfg(feature = "music")] use crate::modules::music::MusicModule; +#[cfg(feature = "networkmanager")] use crate::modules::networkmanager::NetworkmanagerModule; use crate::modules::script::ScriptModule; #[cfg(feature = "sys_info")] @@ -46,6 +47,7 @@ pub enum ModuleConfig { Launcher(Box), #[cfg(feature = "music")] Music(Box), + #[cfg(feature = "networkmanager")] Networkmanager(Box), Script(Box), #[cfg(feature = "sys_info")] From 47a2982b3412793fac661487c313ef326d37b123 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Fri, 29 Mar 2024 20:35:43 +0100 Subject: [PATCH 08/31] refactor: add initial DBus proxy traits --- src/modules/networkmanager.rs | 126 +++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 40 deletions(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 7b59f18..9f74ec1 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -1,12 +1,14 @@ +use std::collections::HashMap; + use color_eyre::Result; use futures_lite::StreamExt; -use gtk::prelude::*; +use gtk::prelude::ContainerExt; use gtk::{Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; -use zbus::fdo::PropertiesProxy; +use zbus::dbus_proxy; use zbus::names::InterfaceName; -use zbus::zvariant::ObjectPath; +use zbus::zvariant::{ObjectPath, Value}; use crate::config::CommonConfig; use crate::gtk_helpers::IronbarGtkExt; @@ -37,6 +39,76 @@ pub enum NetworkmanagerState { WirelessDisconnected, } +#[dbus_proxy( + default_service = "org.freedesktop.NetworkManager", + interface = "org.freedesktop.NetworkManager", + default_path = "/org/freedesktop/NetworkManager" +)] +trait NetworkmanagerDBus { + #[dbus_proxy(property)] + fn active_connections(&self) -> Result>; + + #[dbus_proxy(property)] + fn devices(&self) -> Result>; + + #[dbus_proxy(property)] + fn networking_enabled(&self) -> Result; + + #[dbus_proxy(property)] + fn primary_connection(&self) -> Result; + + #[dbus_proxy(property)] + fn primary_connection_type(&self) -> Result; + + #[dbus_proxy(property)] + fn wireless_enabled(&self) -> Result; +} + +#[dbus_proxy( + default_service = "org.freedesktop.NetworkManager", + interface = "org.freedesktop.DBus.Properties", + default_path = "/org/freedesktop/NetworkManager" +)] +trait NetworkmanagerPropsDBus { + #[dbus_proxy(signal)] + fn properties_changed( + &self, + interface_name: InterfaceName<'s>, + changed_properties: HashMap<&'s str, Value<'s>>, + invalidated_properties: Vec<&'s str>, + ) -> Result<()>; +} + +#[dbus_proxy( + default_service = "org.freedesktop.NetworkManager", + interface = "org.freedesktop.NetworkManager.Connection.Active" +)] +trait ActiveConnectionDBus { + #[dbus_proxy(property)] + fn connection(&self) -> Result; + + #[dbus_proxy(property)] + fn default(&self) -> Result; + + #[dbus_proxy(property)] + fn default6(&self) -> Result; + + #[dbus_proxy(property)] + fn devices(&self) -> Result>; + + #[dbus_proxy(property)] + fn id(&self) -> Result; + + #[dbus_proxy(property)] + fn specific_object(&self) -> Result; + + #[dbus_proxy(property)] + fn type_(&self) -> Result; + + #[dbus_proxy(property)] + fn vpn(&self) -> Result; +} + impl Module for NetworkmanagerModule { type SendMessage = NetworkmanagerState; type ReceiveMessage = (); @@ -55,28 +127,16 @@ impl Module for NetworkmanagerModule { spawn(async move { // TODO: Maybe move this into a `client` à la `upower`? - let nm_proxy = { - let dbus = zbus::Connection::system().await?; - PropertiesProxy::builder(&dbus) - .destination("org.freedesktop.NetworkManager")? - .path("/org/freedesktop/NetworkManager")? - .build() - .await? - }; - let device_interface_name = - InterfaceName::from_static_str("org.freedesktop.NetworkManager")?; + let dbus = zbus::Connection::system().await?; + let nm_proxy = NetworkmanagerDBusProxy::new(&dbus).await?; + let nm_props_proxy = NetworkmanagerPropsDBusProxy::new(&dbus).await?; - let state = get_network_state(&nm_proxy, &device_interface_name).await?; + let state = get_network_state(&nm_proxy).await?; send_async!(tx, ModuleUpdateEvent::Update(state)); - let mut prop_changed_stream = nm_proxy.receive_properties_changed().await?; - while let Some(signal) = prop_changed_stream.next().await { - let args = signal.args()?; - if args.interface_name != device_interface_name { - continue; - } - - let state = get_network_state(&nm_proxy, &device_interface_name).await?; + let mut prop_changed_stream = nm_props_proxy.receive_properties_changed().await?; + while prop_changed_stream.next().await.is_some() { + let state = get_network_state(&nm_proxy).await?; send_async!(tx, ModuleUpdateEvent::Update(state)); } @@ -120,22 +180,10 @@ impl Module for NetworkmanagerModule { } } -async fn get_network_state( - nm_proxy: &PropertiesProxy<'_>, - device_interface_name: &InterfaceName<'_>, -) -> Result { - let properties = nm_proxy.get_all(device_interface_name.clone()).await?; - - let primary_connection_path = properties["PrimaryConnection"] - .downcast_ref::() - .unwrap(); - +async fn get_network_state(nm_proxy: &NetworkmanagerDBusProxy<'_>) -> Result { + let primary_connection_path = nm_proxy.primary_connection().await?; if primary_connection_path != "/" { - let primary_connection_type = properties["PrimaryConnectionType"] - .downcast_ref::() - .unwrap() - .to_string(); - + let primary_connection_type = nm_proxy.primary_connection_type().await?; match primary_connection_type.as_str() { "802-11-olpc-mesh" => Ok(NetworkmanagerState::Wireless), "802-11-wireless" => Ok(NetworkmanagerState::Wireless), @@ -150,9 +198,7 @@ async fn get_network_state( _ => Ok(NetworkmanagerState::Unknown), } } else { - let wireless_enabled = *properties["WirelessEnabled"] - .downcast_ref::() - .unwrap(); + let wireless_enabled = nm_proxy.wireless_enabled().await?; if wireless_enabled { Ok(NetworkmanagerState::WirelessDisconnected) } else { From 288cfeb9a8d404a9aea0dd9787a061736f191656 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Fri, 29 Mar 2024 21:01:36 +0100 Subject: [PATCH 09/31] docs: expand comment about dbus client refactor --- src/modules/networkmanager.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 7b59f18..4786c82 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -54,7 +54,10 @@ impl Module for NetworkmanagerModule { let tx = context.tx.clone(); spawn(async move { - // TODO: Maybe move this into a `client` à la `upower`? + /* TODO: This should be moved into a client à la the upower module, however that + requires additional refactoring as both would request a PropertyProxy but on + different buses. The proper solution will be to rewrite both to use trait-derived + proxies. */ let nm_proxy = { let dbus = zbus::Connection::system().await?; PropertiesProxy::builder(&dbus) From 53c4c7bfc9f0c0fbf9089dd03a40cb60f707bcb3 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sat, 30 Mar 2024 01:40:04 +0100 Subject: [PATCH 10/31] feat: add support for VPN primary connection --- src/modules/networkmanager.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 4786c82..d503176 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -32,6 +32,7 @@ pub enum NetworkmanagerState { Cellular, Offline, Unknown, + Vpn, Wired, Wireless, WirelessDisconnected, @@ -111,6 +112,7 @@ impl Module for NetworkmanagerModule { NetworkmanagerState::Cellular => "network-cellular-symbolic", NetworkmanagerState::Offline => "network-wireless-disabled-symbolic", NetworkmanagerState::Unknown => "dialog-question-symbolic", + NetworkmanagerState::Vpn => "network-vpn-symbolic", NetworkmanagerState::Wired => "network-wired-symbolic", NetworkmanagerState::Wireless => "network-wireless-symbolic", NetworkmanagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", @@ -147,8 +149,10 @@ async fn get_network_state( "cdma" => Ok(NetworkmanagerState::Cellular), "gsm" => Ok(NetworkmanagerState::Cellular), "pppoe" => Ok(NetworkmanagerState::Wired), + "vpn" => Ok(NetworkmanagerState::Vpn), "wifi-p2p" => Ok(NetworkmanagerState::Wireless), "wimax" => Ok(NetworkmanagerState::Cellular), + "wireguard" => Ok(NetworkmanagerState::Vpn), "wpan" => Ok(NetworkmanagerState::Wireless), _ => Ok(NetworkmanagerState::Unknown), } From 417a6be0d3097067dc7b7dc0489986f897f87b4e Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sat, 30 Mar 2024 17:07:33 +0100 Subject: [PATCH 11/31] docs: expand module docs, add note about primary connection limitations --- docs/modules/Networkmanager.md | 80 +++++++++++++++++----------------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/docs/modules/Networkmanager.md b/docs/modules/Networkmanager.md index 1c09ad8..6ace464 100644 --- a/docs/modules/Networkmanager.md +++ b/docs/modules/Networkmanager.md @@ -1,4 +1,10 @@ -Displays network connectivity information. Requires NetworkManager. +Displays the current network connection state of NetworkManager. Supports wired ethernet, wifi, +cellular data and VPN connections among others. + +This module uses NetworkManager's so-called primary connection, and therefore inherits its +limitation of only being able to display the "top-level" connection. For example, if we have a VPN +connection over a wifi connection it will only display the former, until it is disconnected, at +which point it will display the latter. A solution to this is currently in the works. ## Configuration @@ -9,57 +15,53 @@ Displays network connectivity information. Requires NetworkManager. | `icon_size` | `integer` | `24` | Size to render icon at. |
-JSON - -```json -{ - "end": [ - { - "type": "networkmanager", - "icon_size": 32 - } - ] -} -``` + JSON + ```json + { + "end": [ + { + "type": "networkmanager", + "icon_size": 32 + } + ] + } + ```
-TOML - -```toml -[[end]] -type = "networkmanager" -icon_size = 32 -``` + TOML + ```toml + [[end]] + type = "networkmanager" + icon_size = 32 + ```
-YAML - -```yaml -end: - - type: "networkmanager" - icon_size: 32 -``` + YAML + ```yaml + end: + - type: "networkmanager" + icon_size: 32 + ```
-Corn - -```corn -{ - end = [ - { - type = "networkmanager" - icon_size = 32 - } - ] -} -``` + Corn + ```corn + { + end = [ + { + type = "networkmanager" + icon_size = 32 + } + ] + } + ```
## Styling From 20a120727a02e1993551d0e37a3b1805b9124828 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sun, 31 Mar 2024 22:37:58 +0200 Subject: [PATCH 12/31] docs: use note block for primary connection note, remove hardwraps --- docs/modules/Networkmanager.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/modules/Networkmanager.md b/docs/modules/Networkmanager.md index 6ace464..ec3a8fd 100644 --- a/docs/modules/Networkmanager.md +++ b/docs/modules/Networkmanager.md @@ -1,10 +1,10 @@ -Displays the current network connection state of NetworkManager. Supports wired ethernet, wifi, -cellular data and VPN connections among others. +Displays the current network connection state of NetworkManager. +Supports wired ethernet, wifi, cellular data and VPN connections among others. -This module uses NetworkManager's so-called primary connection, and therefore inherits its -limitation of only being able to display the "top-level" connection. For example, if we have a VPN -connection over a wifi connection it will only display the former, until it is disconnected, at -which point it will display the latter. A solution to this is currently in the works. +> [!NOTE] +> This module uses NetworkManager's so-called primary connection, and therefore inherits its limitation of only being able to display the "top-level" connection. +> For example, if we have a VPN connection over a wifi connection it will only display the former, until it is disconnected, at which point it will display the latter. +> A solution to this is currently in the works. ## Configuration From 68ca8da3029cdd6ca20b99b2fa3cd230e291ce24 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sun, 31 Mar 2024 22:42:36 +0200 Subject: [PATCH 13/31] refactor: change NetworkManager module and state capitalisation --- src/config/mod.rs | 4 +-- src/modules/networkmanager.rs | 58 +++++++++++++++++------------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index d791e18..b8bce15 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -15,7 +15,7 @@ use crate::modules::launcher::LauncherModule; #[cfg(feature = "music")] use crate::modules::music::MusicModule; #[cfg(feature = "networkmanager")] -use crate::modules::networkmanager::NetworkmanagerModule; +use crate::modules::networkmanager::NetworkManagerModule; #[cfg(feature = "notifications")] use crate::modules::notifications::NotificationsModule; use crate::modules::script::ScriptModule; @@ -52,7 +52,7 @@ pub enum ModuleConfig { #[cfg(feature = "music")] Music(Box), #[cfg(feature = "networkmanager")] - Networkmanager(Box), + Networkmanager(Box), #[cfg(feature = "notifications")] Notifications(Box), Script(Box), diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index d503176..6422a7e 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -15,7 +15,7 @@ use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetC use crate::{glib_recv, send_async, spawn}; #[derive(Debug, Deserialize, Clone)] -pub struct NetworkmanagerModule { +pub struct NetworkManagerModule { #[serde(default = "default_icon_size")] icon_size: i32, @@ -28,7 +28,7 @@ const fn default_icon_size() -> i32 { } #[derive(Clone, Debug)] -pub enum NetworkmanagerState { +pub enum NetworkManagerState { Cellular, Offline, Unknown, @@ -38,8 +38,8 @@ pub enum NetworkmanagerState { WirelessDisconnected, } -impl Module for NetworkmanagerModule { - type SendMessage = NetworkmanagerState; +impl Module for NetworkManagerModule { + type SendMessage = NetworkManagerState; type ReceiveMessage = (); fn name() -> &'static str { @@ -49,7 +49,7 @@ impl Module for NetworkmanagerModule { fn spawn_controller( &self, _: &ModuleInfo, - context: &WidgetContext, + context: &WidgetContext, _: Receiver<()>, ) -> Result<()> { let tx = context.tx.clone(); @@ -92,7 +92,7 @@ impl Module for NetworkmanagerModule { fn into_widget( self, - context: WidgetContext, + context: WidgetContext, info: &ModuleInfo, ) -> Result> { let container = gtk::Box::new(Orientation::Horizontal, 0); @@ -109,13 +109,13 @@ impl Module for NetworkmanagerModule { let rx = context.subscribe(); glib_recv!(rx, state => { let icon_name = match state { - NetworkmanagerState::Cellular => "network-cellular-symbolic", - NetworkmanagerState::Offline => "network-wireless-disabled-symbolic", - NetworkmanagerState::Unknown => "dialog-question-symbolic", - NetworkmanagerState::Vpn => "network-vpn-symbolic", - NetworkmanagerState::Wired => "network-wired-symbolic", - NetworkmanagerState::Wireless => "network-wireless-symbolic", - NetworkmanagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", + NetworkManagerState::Cellular => "network-cellular-symbolic", + NetworkManagerState::Offline => "network-wireless-disabled-symbolic", + NetworkManagerState::Unknown => "dialog-question-symbolic", + NetworkManagerState::Vpn => "network-vpn-symbolic", + NetworkManagerState::Wired => "network-wired-symbolic", + NetworkManagerState::Wireless => "network-wireless-symbolic", + NetworkManagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", }; ImageProvider::parse(icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); @@ -128,7 +128,7 @@ impl Module for NetworkmanagerModule { async fn get_network_state( nm_proxy: &PropertiesProxy<'_>, device_interface_name: &InterfaceName<'_>, -) -> Result { +) -> Result { let properties = nm_proxy.get_all(device_interface_name.clone()).await?; let primary_connection_path = properties["PrimaryConnection"] @@ -142,28 +142,28 @@ async fn get_network_state( .to_string(); match primary_connection_type.as_str() { - "802-11-olpc-mesh" => Ok(NetworkmanagerState::Wireless), - "802-11-wireless" => Ok(NetworkmanagerState::Wireless), - "802-3-ethernet" => Ok(NetworkmanagerState::Wired), - "adsl" => Ok(NetworkmanagerState::Wired), - "cdma" => Ok(NetworkmanagerState::Cellular), - "gsm" => Ok(NetworkmanagerState::Cellular), - "pppoe" => Ok(NetworkmanagerState::Wired), - "vpn" => Ok(NetworkmanagerState::Vpn), - "wifi-p2p" => Ok(NetworkmanagerState::Wireless), - "wimax" => Ok(NetworkmanagerState::Cellular), - "wireguard" => Ok(NetworkmanagerState::Vpn), - "wpan" => Ok(NetworkmanagerState::Wireless), - _ => Ok(NetworkmanagerState::Unknown), + "802-11-olpc-mesh" => Ok(NetworkManagerState::Wireless), + "802-11-wireless" => Ok(NetworkManagerState::Wireless), + "802-3-ethernet" => Ok(NetworkManagerState::Wired), + "adsl" => Ok(NetworkManagerState::Wired), + "cdma" => Ok(NetworkManagerState::Cellular), + "gsm" => Ok(NetworkManagerState::Cellular), + "pppoe" => Ok(NetworkManagerState::Wired), + "vpn" => Ok(NetworkManagerState::Vpn), + "wifi-p2p" => Ok(NetworkManagerState::Wireless), + "wimax" => Ok(NetworkManagerState::Cellular), + "wireguard" => Ok(NetworkManagerState::Vpn), + "wpan" => Ok(NetworkManagerState::Wireless), + _ => Ok(NetworkManagerState::Unknown), } } else { let wireless_enabled = *properties["WirelessEnabled"] .downcast_ref::() .unwrap(); if wireless_enabled { - Ok(NetworkmanagerState::WirelessDisconnected) + Ok(NetworkManagerState::WirelessDisconnected) } else { - Ok(NetworkmanagerState::Offline) + Ok(NetworkManagerState::Offline) } } } From c41d37ca38dfaf64d7774d52251a0168782d0119 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sun, 31 Mar 2024 22:45:24 +0200 Subject: [PATCH 14/31] fix: add explicit failure message for DBus property downcasting --- src/modules/networkmanager.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 6422a7e..2d98c95 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -56,9 +56,9 @@ impl Module for NetworkManagerModule { spawn(async move { /* TODO: This should be moved into a client à la the upower module, however that - requires additional refactoring as both would request a PropertyProxy but on - different buses. The proper solution will be to rewrite both to use trait-derived - proxies. */ + requires additional refactoring as both would request a PropertyProxy but on + different buses. The proper solution will be to rewrite both to use trait-derived + proxies. */ let nm_proxy = { let dbus = zbus::Connection::system().await?; PropertiesProxy::builder(&dbus) @@ -133,12 +133,12 @@ async fn get_network_state( let primary_connection_path = properties["PrimaryConnection"] .downcast_ref::() - .unwrap(); + .expect("PrimaryConnection was not an object path, violation of NetworkManager D-Bus interface"); if primary_connection_path != "/" { let primary_connection_type = properties["PrimaryConnectionType"] .downcast_ref::() - .unwrap() + .expect("PrimaryConnectionType was not a string, violation of NetworkManager D-Bus interface") .to_string(); match primary_connection_type.as_str() { @@ -159,7 +159,7 @@ async fn get_network_state( } else { let wireless_enabled = *properties["WirelessEnabled"] .downcast_ref::() - .unwrap(); + .expect("WirelessEnabled was not a boolean, violation of NetworkManager D-Bus interface"); if wireless_enabled { Ok(NetworkManagerState::WirelessDisconnected) } else { From e1094ca586deb42a72e65b85fba085aa2ef05f6b Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sun, 31 Mar 2024 22:50:10 +0200 Subject: [PATCH 15/31] refactor: change NetworkManager ModuleConfig capitalisation --- src/bar.rs | 2 +- src/config/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bar.rs b/src/bar.rs index 18cca9d..50a707e 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -386,7 +386,7 @@ fn add_modules( #[cfg(feature = "music")] ModuleConfig::Music(mut module) => add_module!(module, id), #[cfg(feature = "networkmanager")] - ModuleConfig::Networkmanager(mut module) => add_module!(module, id), + ModuleConfig::NetworkManager(mut module) => add_module!(module, id), #[cfg(feature = "notifications")] ModuleConfig::Notifications(mut module) => add_module!(module, id), ModuleConfig::Script(mut module) => add_module!(module, id), diff --git a/src/config/mod.rs b/src/config/mod.rs index b8bce15..9cd139e 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -52,7 +52,7 @@ pub enum ModuleConfig { #[cfg(feature = "music")] Music(Box), #[cfg(feature = "networkmanager")] - Networkmanager(Box), + NetworkManager(Box), #[cfg(feature = "notifications")] Notifications(Box), Script(Box), From 1044da251e27f2621eabdb166fe2310f7069fbf9 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 18 Apr 2024 01:22:55 +0200 Subject: [PATCH 16/31] refactor: introduce networkmanager client --- Cargo.lock | 44 ++++++++ Cargo.toml | 7 +- src/clients/mod.rs | 11 ++ src/clients/networkmanager.rs | 187 ++++++++++++++++++++++++++++++++++ src/modules/networkmanager.rs | 123 ++++------------------ 5 files changed, 269 insertions(+), 103 deletions(-) create mode 100644 src/clients/networkmanager.rs diff --git a/Cargo.lock b/Cargo.lock index c6d55ff..5893805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -784,6 +784,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dlib" version = "0.5.2" @@ -1059,6 +1065,22 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "futures-signals" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b175f2f6600dd81d92d20cf10872b03ea9df6b2513ca7f672341260dacb1ab2" +dependencies = [ + "discard", + "futures-channel", + "futures-core", + "futures-util", + "gensym", + "log", + "pin-project", + "serde", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -1157,6 +1179,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "gensym" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82" +dependencies = [ + "proc-macro2", + "quote 1.0.35", + "syn 2.0.48", + "uuid", +] + [[package]] name = "getrandom" version = "0.2.9" @@ -1669,6 +1703,7 @@ dependencies = [ "ctrlc", "dirs", "futures-lite 2.3.0", + "futures-signals", "futures-util", "glib", "gtk", @@ -3545,6 +3580,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0b70115..d94245a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ music = ["regex"] "music+mpris" = ["music", "mpris"] "music+mpd" = ["music", "mpd-utils"] -networkmanager = ["futures-lite", "zbus"] +networkmanager = ["futures-lite", "futures-signals", "zbus"] notifications = ["zbus"] @@ -130,6 +130,9 @@ chrono = { version = "0.4.35", optional = true, features = ["unstable-locales"] mpd-utils = { version = "0.2.0", optional = true } mpris = { version = "2.0.1", optional = true } +# networkmanager +futures-signals = { version = "0.3.33", optional = true } + # sys_info sysinfo = { version = "0.29.11", optional = true } @@ -137,6 +140,7 @@ sysinfo = { version = "0.29.11", optional = true } system-tray = { version = "0.2.0", optional = true } # upower +futures-lite = { version = "2.3.0", optional = true } upower_dbus = { version = "0.3.2", optional = true } # volume @@ -151,5 +155,4 @@ futures-util = { version = "0.3.30", optional = true } regex = { version = "1.10.4", default-features = false, features = [ "std", ], optional = true } # music, sys_info -futures-lite = { version = "2.3.0", optional = true } # networkmanager, upower zbus = { version = "3.15.2", optional = true } # networkmanager, notifications, upower diff --git a/src/clients/mod.rs b/src/clients/mod.rs index a3899b2..0cfe9cd 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -7,6 +7,8 @@ pub mod clipboard; pub mod compositor; #[cfg(feature = "music")] pub mod music; +#[cfg(feature = "networkmanager")] +pub mod networkmanager; #[cfg(feature = "notifications")] pub mod swaync; #[cfg(feature = "tray")] @@ -28,6 +30,8 @@ pub struct Clients { clipboard: Option>, #[cfg(feature = "music")] music: std::collections::HashMap>, + #[cfg(feature = "networkmanager")] + networkmanager: Option>, #[cfg(feature = "notifications")] notifications: Option>, #[cfg(feature = "tray")] @@ -76,6 +80,13 @@ impl Clients { .clone() } + #[cfg(feature = "networkmanager")] + pub fn networkmanager(&mut self) -> Arc { + self.networkmanager + .get_or_insert_with(networkmanager::create_client) + .clone() + } + #[cfg(feature = "notifications")] pub fn notifications(&mut self) -> Arc { self.notifications diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs new file mode 100644 index 0000000..5192ca6 --- /dev/null +++ b/src/clients/networkmanager.rs @@ -0,0 +1,187 @@ +use std::sync::Arc; + +use futures_signals::signal::{Mutable, MutableSignalCloned}; +use tracing::error; +use zbus::{ + blocking::{fdo::PropertiesProxy, Connection}, + names::InterfaceName, + zvariant::{ObjectPath, Str}, +}; + +use crate::{register_client, spawn_blocking}; + +static DBUS_BUS: &str = "org.freedesktop.NetworkManager"; +static DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; +static DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; + +#[derive(Debug)] +pub struct Client { + client_state: Mutable, +} + +#[derive(Clone, Debug)] +pub enum ClientState { + Unknown, + WiredConnected, + WifiConnected, + CellularConnected, + VpnConnected, + WifiDisconnected, + Offline, +} + +impl Client { + fn new() -> Self { + let client_state = Mutable::new(ClientState::Unknown); + Self { client_state } + } + + fn run(&self) { + let Ok(dbus_connection) = Connection::system() else { + error!("Failed to create D-Bus system connection"); + return; + }; + let builder = PropertiesProxy::builder(&dbus_connection); + let Ok(builder) = builder.destination(DBUS_BUS) else { + error!("Failed to connect to NetworkManager D-Bus bus"); + return; + }; + let Ok(builder) = builder.path(DBUS_PATH) else { + error!("Failed to set to NetworkManager D-Bus path"); + return; + }; + let Ok(props_proxy) = builder.build() else { + error!("Failed to create NetworkManager D-Bus properties proxy"); + return; + }; + let Ok(interface_name) = InterfaceName::from_static_str(DBUS_INTERFACE) else { + error!("Failed to create NetworkManager D-Bus interface name"); + return; + }; + let Ok(changed_props_stream) = props_proxy.receive_properties_changed() else { + error!("Failed to create NetworkManager D-Bus changed properties stream"); + return; + }; + let Ok(props) = props_proxy.get_all(interface_name.clone()) else { + error!("Failed to get NetworkManager D-Bus properties"); + return; + }; + + let mut primary_connection = { + let Some(primary_connection) = props["PrimaryConnection"].downcast_ref::() + else { + error!("PrimaryConnection D-Bus property is not a path"); + return; + }; + primary_connection.to_string() + }; + let mut primary_connection_type = { + let Some(primary_connection_type) = + props["PrimaryConnectionType"].downcast_ref::() + else { + error!("PrimaryConnectionType D-Bus property is not a string"); + return; + }; + primary_connection_type.to_string() + }; + let mut wireless_enabled = { + let Some(wireless_enabled) = props["WirelessEnabled"].downcast_ref::() else { + error!("WirelessEnabled D-Bus property is not a boolean"); + return; + }; + *wireless_enabled + }; + self.client_state.set(determine_state( + &primary_connection, + &primary_connection_type, + wireless_enabled, + )); + + for signal in changed_props_stream { + let Ok(args) = signal.args() else { + error!("Failed to obtain NetworkManager D-Bus changed properties signal arguments"); + return; + }; + if args.interface_name != interface_name { + continue; + } + let changed_props = args.changed_properties; + if let Some(new_primary_connection) = changed_props.get("PrimaryConnection") { + let Some(new_primary_connection) = + new_primary_connection.downcast_ref::() + else { + error!("PrimaryConnection D-Bus property is not a path"); + return; + }; + primary_connection = new_primary_connection.to_string(); + } + if let Some(new_primary_connection_type) = changed_props.get("PrimaryConnectionType") { + let Some(new_primary_connection_type) = + new_primary_connection_type.downcast_ref::() + else { + error!("PrimaryConnectionType D-Bus property is not a string"); + return; + }; + primary_connection_type = new_primary_connection_type.to_string(); + } + if let Some(new_wireless_enabled) = changed_props.get("WirelessEnabled") { + let Some(new_wireless_enabled) = new_wireless_enabled.downcast_ref::() else { + error!("WirelessEnabled D-Bus property is not a string"); + return; + }; + wireless_enabled = *new_wireless_enabled; + } + self.client_state.set(determine_state( + &primary_connection, + &primary_connection_type, + wireless_enabled, + )); + } + } + + pub fn subscribe(&self) -> MutableSignalCloned { + self.client_state.signal_cloned() + } +} + +pub fn create_client() -> Arc { + let client = Arc::new(Client::new()); + { + let client = client.clone(); + spawn_blocking(move || { + client.run(); + }); + } + client +} + +fn determine_state( + primary_connection: &str, + primary_connection_type: &str, + wireless_enabled: bool, +) -> ClientState { + if primary_connection == "/" { + if wireless_enabled { + ClientState::WifiDisconnected + } else { + ClientState::Offline + } + } else { + match primary_connection_type { + "802-11-olpc-mesh" => ClientState::WifiConnected, + "802-11-wireless" => ClientState::WifiConnected, + "802-3-ethernet" => ClientState::WiredConnected, + "adsl" => ClientState::WiredConnected, + "cdma" => ClientState::CellularConnected, + "gsm" => ClientState::CellularConnected, + "pppoe" => ClientState::WiredConnected, + "vpn" => ClientState::VpnConnected, + "wifi-p2p" => ClientState::WifiConnected, + "wimax" => ClientState::CellularConnected, + "wireguard" => ClientState::VpnConnected, + _ => ClientState::Unknown, + } + } +} + +register_client!(Client, networkmanager); diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 2d98c95..45198cd 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -1,13 +1,12 @@ use color_eyre::Result; -use futures_lite::StreamExt; -use gtk::prelude::*; +use futures_signals::signal::SignalExt; +use futures_util::StreamExt; +use gtk::prelude::ContainerExt; use gtk::{Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; -use zbus::fdo::PropertiesProxy; -use zbus::names::InterfaceName; -use zbus::zvariant::ObjectPath; +use crate::clients::networkmanager::{Client, ClientState}; use crate::config::CommonConfig; use crate::gtk_helpers::IronbarGtkExt; use crate::image::ImageProvider; @@ -27,19 +26,8 @@ const fn default_icon_size() -> i32 { 24 } -#[derive(Clone, Debug)] -pub enum NetworkManagerState { - Cellular, - Offline, - Unknown, - Vpn, - Wired, - Wireless, - WirelessDisconnected, -} - impl Module for NetworkManagerModule { - type SendMessage = NetworkManagerState; + type SendMessage = ClientState; type ReceiveMessage = (); fn name() -> &'static str { @@ -49,42 +37,18 @@ impl Module for NetworkManagerModule { fn spawn_controller( &self, _: &ModuleInfo, - context: &WidgetContext, + context: &WidgetContext, _: Receiver<()>, ) -> Result<()> { - let tx = context.tx.clone(); + let client = context.client::(); + let client_signal = client.subscribe(); + let mut client_signal_stream = client_signal.to_stream(); + let widget_transmitter = context.tx.clone(); spawn(async move { - /* TODO: This should be moved into a client à la the upower module, however that - requires additional refactoring as both would request a PropertyProxy but on - different buses. The proper solution will be to rewrite both to use trait-derived - proxies. */ - let nm_proxy = { - let dbus = zbus::Connection::system().await?; - PropertiesProxy::builder(&dbus) - .destination("org.freedesktop.NetworkManager")? - .path("/org/freedesktop/NetworkManager")? - .build() - .await? - }; - let device_interface_name = - InterfaceName::from_static_str("org.freedesktop.NetworkManager")?; - - let state = get_network_state(&nm_proxy, &device_interface_name).await?; - send_async!(tx, ModuleUpdateEvent::Update(state)); - - let mut prop_changed_stream = nm_proxy.receive_properties_changed().await?; - while let Some(signal) = prop_changed_stream.next().await { - let args = signal.args()?; - if args.interface_name != device_interface_name { - continue; - } - - let state = get_network_state(&nm_proxy, &device_interface_name).await?; - send_async!(tx, ModuleUpdateEvent::Update(state)); + while let Some(state) = client_signal_stream.next().await { + send_async!(widget_transmitter, ModuleUpdateEvent::Update(state)); } - - Result::<()>::Ok(()) }); Ok(()) @@ -92,7 +56,7 @@ impl Module for NetworkManagerModule { fn into_widget( self, - context: WidgetContext, + context: WidgetContext, info: &ModuleInfo, ) -> Result> { let container = gtk::Box::new(Orientation::Horizontal, 0); @@ -106,16 +70,16 @@ impl Module for NetworkManagerModule { ImageProvider::parse(initial_icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); - let rx = context.subscribe(); - glib_recv!(rx, state => { + let widget_receiver = context.subscribe(); + glib_recv!(widget_receiver, state => { let icon_name = match state { - NetworkManagerState::Cellular => "network-cellular-symbolic", - NetworkManagerState::Offline => "network-wireless-disabled-symbolic", - NetworkManagerState::Unknown => "dialog-question-symbolic", - NetworkManagerState::Vpn => "network-vpn-symbolic", - NetworkManagerState::Wired => "network-wired-symbolic", - NetworkManagerState::Wireless => "network-wireless-symbolic", - NetworkManagerState::WirelessDisconnected => "network-wireless-acquiring-symbolic", + ClientState::Unknown => "dialog-question-symbolic", + ClientState::WiredConnected => "network-wired-symbolic", + ClientState::WifiConnected => "network-wireless-symbolic", + ClientState::CellularConnected => "network-cellular-symbolic", + ClientState::VpnConnected => "network-vpn-symbolic", + ClientState::WifiDisconnected => "network-wireless-acquiring-symbolic", + ClientState::Offline => "network-wireless-disabled-symbolic", }; ImageProvider::parse(icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); @@ -124,46 +88,3 @@ impl Module for NetworkManagerModule { Ok(ModuleParts::new(container, None)) } } - -async fn get_network_state( - nm_proxy: &PropertiesProxy<'_>, - device_interface_name: &InterfaceName<'_>, -) -> Result { - let properties = nm_proxy.get_all(device_interface_name.clone()).await?; - - let primary_connection_path = properties["PrimaryConnection"] - .downcast_ref::() - .expect("PrimaryConnection was not an object path, violation of NetworkManager D-Bus interface"); - - if primary_connection_path != "/" { - let primary_connection_type = properties["PrimaryConnectionType"] - .downcast_ref::() - .expect("PrimaryConnectionType was not a string, violation of NetworkManager D-Bus interface") - .to_string(); - - match primary_connection_type.as_str() { - "802-11-olpc-mesh" => Ok(NetworkManagerState::Wireless), - "802-11-wireless" => Ok(NetworkManagerState::Wireless), - "802-3-ethernet" => Ok(NetworkManagerState::Wired), - "adsl" => Ok(NetworkManagerState::Wired), - "cdma" => Ok(NetworkManagerState::Cellular), - "gsm" => Ok(NetworkManagerState::Cellular), - "pppoe" => Ok(NetworkManagerState::Wired), - "vpn" => Ok(NetworkManagerState::Vpn), - "wifi-p2p" => Ok(NetworkManagerState::Wireless), - "wimax" => Ok(NetworkManagerState::Cellular), - "wireguard" => Ok(NetworkManagerState::Vpn), - "wpan" => Ok(NetworkManagerState::Wireless), - _ => Ok(NetworkManagerState::Unknown), - } - } else { - let wireless_enabled = *properties["WirelessEnabled"] - .downcast_ref::() - .expect("WirelessEnabled was not a boolean, violation of NetworkManager D-Bus interface"); - if wireless_enabled { - Ok(NetworkManagerState::WirelessDisconnected) - } else { - Ok(NetworkManagerState::Offline) - } - } -} From 8d0560e9cebef6e99bad009f8656609d7b0e23a4 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 18 Apr 2024 02:05:47 +0200 Subject: [PATCH 17/31] fix: post-merge fixes --- Cargo.toml | 2 +- src/config/mod.rs | 2 ++ src/modules/networkmanager.rs | 10 ++++------ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fce6335..5755887 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -157,7 +157,7 @@ swayipc-async = { version = "2.0.1", optional = true } hyprland = { version = "0.3.13", default_features = false, features = ["listener", "tokio", "silent"], optional = true } # shared -futures-lite = { version = "2.3.0", optional = true } # workspaces, upower +futures-lite = { version = "2.3.0", optional = true } # networkmanager, upower, workspaces regex = { version = "1.10.4", default-features = false, features = [ "std", ], optional = true } # music, sys_info diff --git a/src/config/mod.rs b/src/config/mod.rs index a5eb6e8..03c4daf 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -103,6 +103,8 @@ impl ModuleConfig { Self::Launcher(module) => create!(module), #[cfg(feature = "music")] Self::Music(module) => create!(module), + #[cfg(feature = "networkmanager")] + Self::NetworkManager(module) => create!(module), #[cfg(feature = "notifications")] Self::Notifications(module) => create!(module), Self::Script(module) => create!(module), diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 45198cd..b1f456d 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -1,6 +1,6 @@ use color_eyre::Result; +use futures_lite::StreamExt; use futures_signals::signal::SignalExt; -use futures_util::StreamExt; use gtk::prelude::ContainerExt; use gtk::{Image, Orientation}; use serde::Deserialize; @@ -11,7 +11,7 @@ use crate::config::CommonConfig; use crate::gtk_helpers::IronbarGtkExt; use crate::image::ImageProvider; use crate::modules::{Module, ModuleInfo, ModuleParts, ModuleUpdateEvent, WidgetContext}; -use crate::{glib_recv, send_async, spawn}; +use crate::{glib_recv, module_impl, send_async, spawn}; #[derive(Debug, Deserialize, Clone)] pub struct NetworkManagerModule { @@ -30,10 +30,6 @@ impl Module for NetworkManagerModule { type SendMessage = ClientState; type ReceiveMessage = (); - fn name() -> &'static str { - "networkmanager" - } - fn spawn_controller( &self, _: &ModuleInfo, @@ -87,4 +83,6 @@ impl Module for NetworkManagerModule { Ok(ModuleParts::new(container, None)) } + + module_impl!("networkmanager"); } From 59a5166effeaf9d2ae77b15d53ab1e2366c6dfac Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Thu, 18 Apr 2024 13:46:10 +0200 Subject: [PATCH 18/31] refactor: remove unnecessary intermediate variable --- src/modules/networkmanager.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index b1f456d..66c20a0 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -37,12 +37,11 @@ impl Module for NetworkManagerModule { _: Receiver<()>, ) -> Result<()> { let client = context.client::(); - let client_signal = client.subscribe(); - let mut client_signal_stream = client_signal.to_stream(); + let mut client_signal = client.subscribe().to_stream(); let widget_transmitter = context.tx.clone(); spawn(async move { - while let Some(state) = client_signal_stream.next().await { + while let Some(state) = client_signal.next().await { send_async!(widget_transmitter, ModuleUpdateEvent::Update(state)); } }); From 6478dd62fbcbca162dbdf7f747753198ce619ec4 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Mon, 22 Apr 2024 15:50:31 +0200 Subject: [PATCH 19/31] refactor: networkmanager rewritten to utilise fallible_client --- src/clients/mod.rs | 13 +++- src/clients/networkmanager.rs | 138 +++++++++++++--------------------- src/modules/networkmanager.rs | 2 +- 3 files changed, 63 insertions(+), 90 deletions(-) diff --git a/src/clients/mod.rs b/src/clients/mod.rs index cc0c5e6..d286bdb 100644 --- a/src/clients/mod.rs +++ b/src/clients/mod.rs @@ -101,10 +101,15 @@ impl Clients { } #[cfg(feature = "networkmanager")] - pub fn networkmanager(&mut self) -> Arc { - self.networkmanager - .get_or_insert_with(networkmanager::create_client) - .clone() + pub fn networkmanager(&mut self) -> ClientResult { + match &self.networkmanager { + Some(client) => Ok(client.clone()), + None => { + let client = networkmanager::create_client()?; + self.networkmanager = Some(client.clone()); + Ok(client) + } + } } #[cfg(feature = "notifications")] diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs index 5192ca6..5f4fa65 100644 --- a/src/clients/networkmanager.rs +++ b/src/clients/networkmanager.rs @@ -1,14 +1,16 @@ use std::sync::Arc; +use color_eyre::Result; use futures_signals::signal::{Mutable, MutableSignalCloned}; use tracing::error; use zbus::{ blocking::{fdo::PropertiesProxy, Connection}, names::InterfaceName, - zvariant::{ObjectPath, Str}, + zvariant::{Error as ZVariantError, ObjectPath, Str}, + Error as ZBusError, }; -use crate::{register_client, spawn_blocking}; +use crate::{register_fallible_client, spawn_blocking}; static DBUS_BUS: &str = "org.freedesktop.NetworkManager"; static DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; @@ -17,6 +19,8 @@ static DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; #[derive(Debug)] pub struct Client { client_state: Mutable, + interface_name: InterfaceName<'static>, + props_proxy: PropertiesProxy<'static>, } #[derive(Clone, Debug)] @@ -31,104 +35,64 @@ pub enum ClientState { } impl Client { - fn new() -> Self { + fn new() -> Result { let client_state = Mutable::new(ClientState::Unknown); - Self { client_state } + let dbus_connection = Connection::system()?; + let props_proxy = PropertiesProxy::builder(&dbus_connection) + .destination(DBUS_BUS)? + .path(DBUS_PATH)? + .build()?; + let interface_name = InterfaceName::from_static_str(DBUS_INTERFACE)?; + + Ok(Self { + client_state, + interface_name, + props_proxy, + }) } - fn run(&self) { - let Ok(dbus_connection) = Connection::system() else { - error!("Failed to create D-Bus system connection"); - return; - }; - let builder = PropertiesProxy::builder(&dbus_connection); - let Ok(builder) = builder.destination(DBUS_BUS) else { - error!("Failed to connect to NetworkManager D-Bus bus"); - return; - }; - let Ok(builder) = builder.path(DBUS_PATH) else { - error!("Failed to set to NetworkManager D-Bus path"); - return; - }; - let Ok(props_proxy) = builder.build() else { - error!("Failed to create NetworkManager D-Bus properties proxy"); - return; - }; - let Ok(interface_name) = InterfaceName::from_static_str(DBUS_INTERFACE) else { - error!("Failed to create NetworkManager D-Bus interface name"); - return; - }; - let Ok(changed_props_stream) = props_proxy.receive_properties_changed() else { - error!("Failed to create NetworkManager D-Bus changed properties stream"); - return; - }; - let Ok(props) = props_proxy.get_all(interface_name.clone()) else { - error!("Failed to get NetworkManager D-Bus properties"); - return; - }; - - let mut primary_connection = { - let Some(primary_connection) = props["PrimaryConnection"].downcast_ref::() - else { - error!("PrimaryConnection D-Bus property is not a path"); - return; - }; - primary_connection.to_string() - }; - let mut primary_connection_type = { - let Some(primary_connection_type) = - props["PrimaryConnectionType"].downcast_ref::() - else { - error!("PrimaryConnectionType D-Bus property is not a string"); - return; - }; - primary_connection_type.to_string() - }; - let mut wireless_enabled = { - let Some(wireless_enabled) = props["WirelessEnabled"].downcast_ref::() else { - error!("WirelessEnabled D-Bus property is not a boolean"); - return; - }; - *wireless_enabled - }; + fn run(&self) -> Result<()> { + let props = self.props_proxy.get_all(self.interface_name.clone())?; + let mut primary_connection = props["PrimaryConnection"] + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))? + .to_string(); + let mut primary_connection_type = props["PrimaryConnectionType"] + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))? + .to_string(); + let mut wireless_enabled = *props["WirelessEnabled"] + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))?; self.client_state.set(determine_state( &primary_connection, &primary_connection_type, wireless_enabled, )); + let changed_props_stream = self.props_proxy.receive_properties_changed()?; for signal in changed_props_stream { - let Ok(args) = signal.args() else { - error!("Failed to obtain NetworkManager D-Bus changed properties signal arguments"); - return; - }; - if args.interface_name != interface_name { + let args = signal.args()?; + if args.interface_name != self.interface_name { continue; } let changed_props = args.changed_properties; if let Some(new_primary_connection) = changed_props.get("PrimaryConnection") { - let Some(new_primary_connection) = - new_primary_connection.downcast_ref::() - else { - error!("PrimaryConnection D-Bus property is not a path"); - return; - }; + let new_primary_connection = new_primary_connection + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))?; primary_connection = new_primary_connection.to_string(); } if let Some(new_primary_connection_type) = changed_props.get("PrimaryConnectionType") { - let Some(new_primary_connection_type) = - new_primary_connection_type.downcast_ref::() - else { - error!("PrimaryConnectionType D-Bus property is not a string"); - return; - }; + let new_primary_connection_type = new_primary_connection_type + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))?; primary_connection_type = new_primary_connection_type.to_string(); } if let Some(new_wireless_enabled) = changed_props.get("WirelessEnabled") { - let Some(new_wireless_enabled) = new_wireless_enabled.downcast_ref::() else { - error!("WirelessEnabled D-Bus property is not a string"); - return; - }; + let new_wireless_enabled = new_wireless_enabled + .downcast_ref::() + .ok_or(ZBusError::Variant(ZVariantError::IncorrectType))?; wireless_enabled = *new_wireless_enabled; } self.client_state.set(determine_state( @@ -137,6 +101,8 @@ impl Client { wireless_enabled, )); } + + Ok(()) } pub fn subscribe(&self) -> MutableSignalCloned { @@ -144,15 +110,17 @@ impl Client { } } -pub fn create_client() -> Arc { - let client = Arc::new(Client::new()); +pub fn create_client() -> Result> { + let client = Arc::new(Client::new()?); { let client = client.clone(); spawn_blocking(move || { - client.run(); + if let Err(error) = client.run() { + error!("{}", error) + }; }); } - client + Ok(client) } fn determine_state( @@ -184,4 +152,4 @@ fn determine_state( } } -register_client!(Client, networkmanager); +register_fallible_client!(Client, networkmanager); diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 66c20a0..1d93777 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -36,7 +36,7 @@ impl Module for NetworkManagerModule { context: &WidgetContext, _: Receiver<()>, ) -> Result<()> { - let client = context.client::(); + let client = context.try_client::()?; let mut client_signal = client.subscribe().to_stream(); let widget_transmitter = context.tx.clone(); From 6a330136f496f6fc71af582fc402b03d55fca520 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Wed, 24 Apr 2024 14:08:08 +0200 Subject: [PATCH 20/31] refactor: make client state enum order consistent --- src/clients/networkmanager.rs | 19 ++++++------------- src/modules/networkmanager.rs | 12 ++++++------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs index 5f4fa65..a1b760f 100644 --- a/src/clients/networkmanager.rs +++ b/src/clients/networkmanager.rs @@ -25,13 +25,13 @@ pub struct Client { #[derive(Clone, Debug)] pub enum ClientState { - Unknown, WiredConnected, WifiConnected, CellularConnected, VpnConnected, WifiDisconnected, Offline, + Unknown, } impl Client { @@ -116,7 +116,7 @@ pub fn create_client() -> Result> { let client = client.clone(); spawn_blocking(move || { if let Err(error) = client.run() { - error!("{}", error) + error!("{}", error); }; }); } @@ -136,17 +136,10 @@ fn determine_state( } } else { match primary_connection_type { - "802-11-olpc-mesh" => ClientState::WifiConnected, - "802-11-wireless" => ClientState::WifiConnected, - "802-3-ethernet" => ClientState::WiredConnected, - "adsl" => ClientState::WiredConnected, - "cdma" => ClientState::CellularConnected, - "gsm" => ClientState::CellularConnected, - "pppoe" => ClientState::WiredConnected, - "vpn" => ClientState::VpnConnected, - "wifi-p2p" => ClientState::WifiConnected, - "wimax" => ClientState::CellularConnected, - "wireguard" => ClientState::VpnConnected, + "802-3-ethernet" | "adsl" | "pppoe" => ClientState::WiredConnected, + "802-11-olpc-mesh" | "802-11-wireless" | "wifi-p2p" => ClientState::WifiConnected, + "cdma" | "gsm" | "wimax" => ClientState::CellularConnected, + "vpn" | "wireguard" => ClientState::VpnConnected, _ => ClientState::Unknown, } } diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 1d93777..972fccb 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -2,7 +2,7 @@ use color_eyre::Result; use futures_lite::StreamExt; use futures_signals::signal::SignalExt; use gtk::prelude::ContainerExt; -use gtk::{Image, Orientation}; +use gtk::{Box, Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; @@ -26,7 +26,7 @@ const fn default_icon_size() -> i32 { 24 } -impl Module for NetworkManagerModule { +impl Module for NetworkManagerModule { type SendMessage = ClientState; type ReceiveMessage = (); @@ -53,28 +53,28 @@ impl Module for NetworkManagerModule { self, context: WidgetContext, info: &ModuleInfo, - ) -> Result> { - let container = gtk::Box::new(Orientation::Horizontal, 0); + ) -> Result> { + let container = Box::new(Orientation::Horizontal, 0); let icon = Image::new(); icon.add_class("icon"); container.add(&icon); let icon_theme = info.icon_theme.clone(); - let initial_icon_name = "icon:content-loading-symbolic"; + let initial_icon_name = "content-loading-symbolic"; ImageProvider::parse(initial_icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); let widget_receiver = context.subscribe(); glib_recv!(widget_receiver, state => { let icon_name = match state { - ClientState::Unknown => "dialog-question-symbolic", ClientState::WiredConnected => "network-wired-symbolic", ClientState::WifiConnected => "network-wireless-symbolic", ClientState::CellularConnected => "network-cellular-symbolic", ClientState::VpnConnected => "network-vpn-symbolic", ClientState::WifiDisconnected => "network-wireless-acquiring-symbolic", ClientState::Offline => "network-wireless-disabled-symbolic", + ClientState::Unknown => "dialog-question-symbolic", }; ImageProvider::parse(icon_name, &icon_theme, false, self.icon_size) .map(|provider| provider.load_into_image(icon.clone())); From 33f73eefa066a56dc01321895b2f32fa97a515a7 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 14 May 2024 19:02:16 +0200 Subject: [PATCH 21/31] Add GTK Box import alias, change DBus strings to const --- src/clients/networkmanager.rs | 6 +++--- src/modules/networkmanager.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs index a1b760f..32487c0 100644 --- a/src/clients/networkmanager.rs +++ b/src/clients/networkmanager.rs @@ -12,9 +12,9 @@ use zbus::{ use crate::{register_fallible_client, spawn_blocking}; -static DBUS_BUS: &str = "org.freedesktop.NetworkManager"; -static DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; -static DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; +const DBUS_BUS: &str = "org.freedesktop.NetworkManager"; +const DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; +const DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; #[derive(Debug)] pub struct Client { diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 972fccb..16e8239 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -2,7 +2,7 @@ use color_eyre::Result; use futures_lite::StreamExt; use futures_signals::signal::SignalExt; use gtk::prelude::ContainerExt; -use gtk::{Box, Image, Orientation}; +use gtk::{Box as GtkBox, Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; @@ -26,7 +26,7 @@ const fn default_icon_size() -> i32 { 24 } -impl Module for NetworkManagerModule { +impl Module for NetworkManagerModule { type SendMessage = ClientState; type ReceiveMessage = (); @@ -53,8 +53,8 @@ impl Module for NetworkManagerModule { self, context: WidgetContext, info: &ModuleInfo, - ) -> Result> { - let container = Box::new(Orientation::Horizontal, 0); + ) -> Result> { + let container = GtkBox::new(Orientation::Horizontal, 0); let icon = Image::new(); icon.add_class("icon"); container.add(&icon); From 17e830afbf9eb09ee2a51f0adec207a29e73c003 Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 14 May 2024 19:05:19 +0200 Subject: [PATCH 22/31] Revert "Add GTK Box import alias, change DBus strings to const" This reverts commit 33f73eefa066a56dc01321895b2f32fa97a515a7. --- src/clients/networkmanager.rs | 6 +++--- src/modules/networkmanager.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs index 32487c0..a1b760f 100644 --- a/src/clients/networkmanager.rs +++ b/src/clients/networkmanager.rs @@ -12,9 +12,9 @@ use zbus::{ use crate::{register_fallible_client, spawn_blocking}; -const DBUS_BUS: &str = "org.freedesktop.NetworkManager"; -const DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; -const DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; +static DBUS_BUS: &str = "org.freedesktop.NetworkManager"; +static DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; +static DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; #[derive(Debug)] pub struct Client { diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 16e8239..972fccb 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -2,7 +2,7 @@ use color_eyre::Result; use futures_lite::StreamExt; use futures_signals::signal::SignalExt; use gtk::prelude::ContainerExt; -use gtk::{Box as GtkBox, Image, Orientation}; +use gtk::{Box, Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; @@ -26,7 +26,7 @@ const fn default_icon_size() -> i32 { 24 } -impl Module for NetworkManagerModule { +impl Module for NetworkManagerModule { type SendMessage = ClientState; type ReceiveMessage = (); @@ -53,8 +53,8 @@ impl Module for NetworkManagerModule { self, context: WidgetContext, info: &ModuleInfo, - ) -> Result> { - let container = GtkBox::new(Orientation::Horizontal, 0); + ) -> Result> { + let container = Box::new(Orientation::Horizontal, 0); let icon = Image::new(); icon.add_class("icon"); container.add(&icon); From 00df5aa79e39d1c8e10e048849f6934b60b90e4f Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Tue, 14 May 2024 19:11:24 +0200 Subject: [PATCH 23/31] Add GTK Box import alias, change DBus strings to const --- src/clients/networkmanager.rs | 6 +++--- src/modules/networkmanager.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs index a1b760f..32487c0 100644 --- a/src/clients/networkmanager.rs +++ b/src/clients/networkmanager.rs @@ -12,9 +12,9 @@ use zbus::{ use crate::{register_fallible_client, spawn_blocking}; -static DBUS_BUS: &str = "org.freedesktop.NetworkManager"; -static DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; -static DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; +const DBUS_BUS: &str = "org.freedesktop.NetworkManager"; +const DBUS_PATH: &str = "/org/freedesktop/NetworkManager"; +const DBUS_INTERFACE: &str = "org.freedesktop.NetworkManager"; #[derive(Debug)] pub struct Client { diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 972fccb..16e8239 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -2,7 +2,7 @@ use color_eyre::Result; use futures_lite::StreamExt; use futures_signals::signal::SignalExt; use gtk::prelude::ContainerExt; -use gtk::{Box, Image, Orientation}; +use gtk::{Box as GtkBox, Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; @@ -26,7 +26,7 @@ const fn default_icon_size() -> i32 { 24 } -impl Module for NetworkManagerModule { +impl Module for NetworkManagerModule { type SendMessage = ClientState; type ReceiveMessage = (); @@ -53,8 +53,8 @@ impl Module for NetworkManagerModule { self, context: WidgetContext, info: &ModuleInfo, - ) -> Result> { - let container = Box::new(Orientation::Horizontal, 0); + ) -> Result> { + let container = GtkBox::new(Orientation::Horizontal, 0); let icon = Image::new(); icon.add_class("icon"); container.add(&icon); From da94923f737d33fcf8f6b97ed189557df788239d Mon Sep 17 00:00:00 2001 From: Reinout Meliesie Date: Sat, 18 May 2024 20:11:43 +0200 Subject: [PATCH 24/31] Replace text with icons in volume module --- src/modules/volume.rs | 151 ++++++++++++++++++++---------------------- 1 file changed, 71 insertions(+), 80 deletions(-) diff --git a/src/modules/volume.rs b/src/modules/volume.rs index f11fa60..c9b6ef9 100644 --- a/src/modules/volume.rs +++ b/src/modules/volume.rs @@ -1,6 +1,7 @@ use crate::clients::volume::{self, Event}; use crate::config::CommonConfig; use crate::gtk_helpers::IronbarGtkExt; +use crate::image::ImageProvider; use crate::modules::{ Module, ModuleInfo, ModuleParts, ModulePopup, ModuleUpdateEvent, PopupButton, WidgetContext, }; @@ -8,81 +9,32 @@ use crate::{glib_recv, lock, module_impl, send_async, spawn, try_send}; use glib::Propagation; use gtk::pango::EllipsizeMode; use gtk::prelude::*; -use gtk::{Button, CellRendererText, ComboBoxText, Label, Orientation, Scale, ToggleButton}; +use gtk::{ + Box as GtkBox, Button, CellRendererText, ComboBoxText, Image, Label, Orientation, Scale, + ToggleButton, +}; use serde::Deserialize; use std::collections::HashMap; use tokio::sync::mpsc; #[derive(Debug, Clone, Deserialize)] pub struct VolumeModule { - #[serde(default = "default_format")] - format: String, - #[serde(default = "default_max_volume")] max_volume: f64, - #[serde(default)] - icons: Icons, + #[serde(default = "default_icon_size")] + icon_size: i32, #[serde(flatten)] pub common: Option, } -fn default_format() -> String { - String::from("{icon} {percentage}%") -} - -#[derive(Debug, Clone, Deserialize)] -pub struct Icons { - #[serde(default = "default_icon_volume_high")] - volume_high: String, - #[serde(default = "default_icon_volume_medium")] - volume_medium: String, - #[serde(default = "default_icon_volume_low")] - volume_low: String, - #[serde(default = "default_icon_muted")] - muted: String, -} - -impl Icons { - fn volume_icon(&self, volume_percent: f64) -> &str { - match volume_percent as u32 { - 0..=33 => &self.volume_low, - 34..=66 => &self.volume_medium, - 67.. => &self.volume_high, - } - } -} - -impl Default for Icons { - fn default() -> Self { - Self { - volume_high: default_icon_volume_high(), - volume_medium: default_icon_volume_medium(), - volume_low: default_icon_volume_low(), - muted: default_icon_muted(), - } - } -} - const fn default_max_volume() -> f64 { 100.0 } -fn default_icon_volume_high() -> String { - String::from("󰕾") -} - -fn default_icon_volume_medium() -> String { - String::from("󰖀") -} - -fn default_icon_volume_low() -> String { - String::from("󰕿") -} - -fn default_icon_muted() -> String { - String::from("󰝟") +const fn default_icon_size() -> i32 { + 24 } #[derive(Debug, Clone)] @@ -185,22 +137,23 @@ impl Module