diff --git a/docs/modules/Networkmanager.md b/docs/modules/Networkmanager.md deleted file mode 100644 index bd318d9..0000000 --- a/docs/modules/Networkmanager.md +++ /dev/null @@ -1,74 +0,0 @@ -Displays the current network connection state of NetworkManager. -Supports wired ethernet, wifi, cellular data and VPN connections among others. - -> [!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 - -> 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 icons. | - -For more information on styling, please see the [styling guide](styling-guide). diff --git a/src/clients/networkmanager.rs b/src/clients/networkmanager.rs deleted file mode 100644 index 79b1f45..0000000 --- a/src/clients/networkmanager.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::sync::Arc; - -use color_eyre::Result; -use futures_signals::signal::{Mutable, MutableSignalCloned}; -use tracing::error; -use zbus::blocking::fdo::PropertiesProxy; -use zbus::blocking::Connection; -use zbus::{ - dbus_proxy, - names::InterfaceName, - zvariant::{ObjectPath, Str}, -}; - -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"; - -#[derive(Debug)] -pub struct Client { - client_state: Mutable, - interface_name: InterfaceName<'static>, - dbus_connection: Connection, - props_proxy: PropertiesProxy<'static>, -} - -#[derive(Clone, Debug)] -pub enum ClientState { - WiredConnected, - WifiConnected, - CellularConnected, - VpnConnected, - WifiDisconnected, - Offline, - Unknown, -} - -#[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; -} - -impl Client { - fn new() -> Result { - let client_state = Mutable::new(ClientState::Unknown); - let dbus_connection = Connection::system()?; - let interface_name = InterfaceName::from_static_str(DBUS_INTERFACE)?; - let props_proxy = PropertiesProxy::builder(&dbus_connection) - .destination(DBUS_BUS)? - .path(DBUS_PATH)? - .build()?; - - Ok(Self { - client_state, - interface_name, - dbus_connection, - props_proxy, - }) - } - - fn run(&self) -> Result<()> { - let proxy = NetworkManagerDbusProxyBlocking::new(&self.dbus_connection)?; - - let mut primary_connection = proxy.primary_connection()?; - let mut primary_connection_type = proxy.primary_connection_type()?; - let mut wireless_enabled = proxy.wireless_enabled()?; - - self.client_state.set(determine_state( - &primary_connection, - &primary_connection_type, - wireless_enabled, - )); - - for change in self.props_proxy.receive_properties_changed()? { - let args = change.args()?; - if args.interface_name != self.interface_name { - continue; - } - - let changed_props = args.changed_properties; - let mut relevant_prop_changed = false; - - if changed_props.contains_key("PrimaryConnection") { - primary_connection = proxy.primary_connection()?; - relevant_prop_changed = true; - } - if changed_props.contains_key("PrimaryConnectionType") { - primary_connection_type = proxy.primary_connection_type()?; - relevant_prop_changed = true; - } - if changed_props.contains_key("WirelessEnabled") { - wireless_enabled = proxy.wireless_enabled()?; - relevant_prop_changed = true; - } - - if relevant_prop_changed { - self.client_state.set(determine_state( - &primary_connection, - &primary_connection_type, - wireless_enabled, - )); - } - } - - Ok(()) - } - - pub fn subscribe(&self) -> MutableSignalCloned { - self.client_state.signal_cloned() - } -} - -pub fn create_client() -> Result> { - let client = Arc::new(Client::new()?); - { - let client = client.clone(); - spawn_blocking(move || { - if let Err(error) = client.run() { - error!("{}", error); - }; - }); - } - Ok(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-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, - } - } -} - -register_fallible_client!(Client, network_manager); diff --git a/src/clients/networkmanager/mod.rs b/src/clients/networkmanager/mod.rs index b0ac6a2..40c64e1 100644 --- a/src/clients/networkmanager/mod.rs +++ b/src/clients/networkmanager/mod.rs @@ -232,4 +232,4 @@ pub fn create_client() -> Result> { Ok(client) } -register_fallible_client!(Client, networkmanager); +register_fallible_client!(Client, network_manager); diff --git a/src/modules/networkmanager.rs b/src/modules/networkmanager.rs index 6235b51..a30be1d 100644 --- a/src/modules/networkmanager.rs +++ b/src/modules/networkmanager.rs @@ -1,12 +1,15 @@ use color_eyre::Result; use futures_lite::StreamExt; use futures_signals::signal::SignalExt; -use gtk::prelude::ContainerExt; +use gtk::prelude::{ContainerExt, WidgetExt}; use gtk::{Box as GtkBox, Image, Orientation}; use serde::Deserialize; use tokio::sync::mpsc::Receiver; -use crate::clients::networkmanager::{Client, ClientState}; +use crate::clients::networkmanager::state::{ + CellularState, State, VpnState, WifiState, WiredState, +}; +use crate::clients::networkmanager::Client; use crate::config::CommonConfig; use crate::gtk_helpers::IronbarGtkExt; use crate::image::ImageProvider; @@ -28,7 +31,7 @@ const fn default_icon_size() -> i32 { } impl Module for NetworkManagerModule { - type SendMessage = ClientState; + type SendMessage = State; type ReceiveMessage = (); module_impl!("network_manager"); @@ -36,7 +39,7 @@ impl Module for NetworkManagerModule { fn spawn_controller( &self, _: &ModuleInfo, - context: &WidgetContext, + context: &WidgetContext, _: Receiver<()>, ) -> Result<()> { let client = context.try_client::()?; @@ -54,33 +57,77 @@ impl Module for NetworkManagerModule { fn into_widget( self, - context: WidgetContext, + context: WidgetContext, info: &ModuleInfo, ) -> Result> { let container = GtkBox::new(Orientation::Horizontal, 0); - let icon = Image::new(); - icon.add_class("icon"); - container.add(&icon); + + // Wired icon + let wired_icon = Image::new(); + wired_icon.add_class("icon"); + wired_icon.add_class("wired-icon"); + container.add(&wired_icon); + + // Wifi icon + let wifi_icon = Image::new(); + wifi_icon.add_class("icon"); + wifi_icon.add_class("wifi-icon"); + container.add(&wifi_icon); + + // Cellular icon + let cellular_icon = Image::new(); + cellular_icon.add_class("icon"); + cellular_icon.add_class("cellular-icon"); + container.add(&cellular_icon); + + // VPN icon + let vpn_icon = Image::new(); + vpn_icon.add_class("icon"); + vpn_icon.add_class("vpn-icon"); + container.add(&vpn_icon); let icon_theme = info.icon_theme.clone(); + glib_recv!(context.subscribe(), state => { + macro_rules! update_icon { + ( + $icon_var:expr, + $state_type:ident, + {$($state:pat => $icon_name:expr,)+} + ) => { + let icon_name = match state.$state_type { + $($state => $icon_name,)+ + }; + if icon_name.is_empty() { + $icon_var.hide(); + } else { + ImageProvider::parse(icon_name, &icon_theme, false, self.icon_size) + .map(|provider| provider.load_into_image($icon_var.clone())); + $icon_var.show(); + } + }; + } - 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::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())); + update_icon!(wired_icon, wired, { + WiredState::Connected => "icon:network-wired-symbolic", + WiredState::Disconnected => "icon:network-wired-disconnected-symbolic", + WiredState::NotPresent | WiredState::Unknown => "", + }); + update_icon!(wifi_icon, wifi, { + WifiState::Connected(_) => "icon:network-wireless-connected-symbolic", + WifiState::Disconnected => "icon:network-wireless-offline-symbolic", + WifiState::Disabled => "icon:network-wireless-hardware-disabled-symbolic", + WifiState::NotPresent | WifiState::Unknown => "", + }); + update_icon!(cellular_icon, cellular, { + CellularState::Connected => "icon:network-cellular-connected-symbolic", + CellularState::Disconnected => "icon:network-cellular-offline-symbolic", + CellularState::Disabled => "icon:network-cellular-hardware-disabled-symbolic", + CellularState::NotPresent | CellularState::Unknown => "", + }); + update_icon!(vpn_icon, vpn, { + VpnState::Connected(_) => "icon:network-vpn-symbolic", + VpnState::Disconnected | VpnState::Unknown => "", + }); }); Ok(ModuleParts::new(container, None))