mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-04-19 19:34:24 +02:00
feat: actually implement icon per connection type
This commit is contained in:
parent
cd0991e865
commit
4182038643
6 changed files with 529 additions and 300 deletions
|
@ -69,6 +69,6 @@ Supports wired ethernet, wifi, cellular data and VPN connections among others.
|
||||||
| Selector | Description |
|
| Selector | Description |
|
||||||
|------------------------|----------------------------------|
|
|------------------------|----------------------------------|
|
||||||
| `.networkmanager` | NetworkManager widget container. |
|
| `.networkmanager` | NetworkManager widget container. |
|
||||||
| `.networkmanger .icon` | NetworkManager widget icon. |
|
| `.networkmanger .icon` | NetworkManager widget icons. |
|
||||||
|
|
||||||
For more information on styling, please see the [styling guide](styling-guide).
|
For more information on styling, please see the [styling guide](styling-guide).
|
||||||
|
|
|
@ -1,296 +0,0 @@
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
use futures_signals::signal::{Mutable, MutableSignalCloned};
|
|
||||||
use tracing::error;
|
|
||||||
use zbus::blocking::Connection;
|
|
||||||
use zbus::{
|
|
||||||
dbus_proxy,
|
|
||||||
zvariant::{ObjectPath, Str},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
read_lock, register_fallible_client, spawn_blocking, spawn_blocking_result, write_lock,
|
|
||||||
};
|
|
||||||
|
|
||||||
// States
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct State {
|
|
||||||
pub wired: WiredState,
|
|
||||||
pub wifi: WifiState,
|
|
||||||
pub cellular: CellularState,
|
|
||||||
pub vpn: VpnState,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum WiredState {
|
|
||||||
Connected,
|
|
||||||
Disconnected,
|
|
||||||
NotPresent,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum WifiState {
|
|
||||||
Connected(WifiConnectedState),
|
|
||||||
Disconnected,
|
|
||||||
Disabled,
|
|
||||||
NotPresent,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct WifiConnectedState {
|
|
||||||
pub ssid: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum CellularState {
|
|
||||||
Connected,
|
|
||||||
Disconnected,
|
|
||||||
Disabled,
|
|
||||||
NotPresent,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum VpnState {
|
|
||||||
Connected(VpnConnectedState),
|
|
||||||
Disconnected,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct VpnConnectedState {
|
|
||||||
pub name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
// D-Bus interfaces
|
|
||||||
|
|
||||||
#[dbus_proxy(
|
|
||||||
default_service = "org.freedesktop.NetworkManager",
|
|
||||||
interface = "org.freedesktop.NetworkManager",
|
|
||||||
default_path = "/org/freedesktop/NetworkManager"
|
|
||||||
)]
|
|
||||||
trait Dbus {
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn active_connections(&self) -> Result<Vec<ObjectPath>>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn devices(&self) -> Result<Vec<ObjectPath>>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn networking_enabled(&self) -> Result<bool>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn primary_connection(&self) -> Result<ObjectPath>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn primary_connection_type(&self) -> Result<Str>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn wireless_enabled(&self) -> Result<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[dbus_proxy(
|
|
||||||
default_service = "org.freedesktop.NetworkManager",
|
|
||||||
interface = "org.freedesktop.NetworkManager.Connection.Active"
|
|
||||||
)]
|
|
||||||
trait ActiveConnectionDbus {
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn connection(&self) -> Result<ObjectPath>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn devices(&self) -> Result<Vec<ObjectPath>>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn id(&self) -> Result<Str>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn specific_object(&self) -> Result<ObjectPath>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn type_(&self) -> Result<Str>;
|
|
||||||
|
|
||||||
#[dbus_proxy(property)]
|
|
||||||
fn uuid(&self) -> Result<Str>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ironbar client & helpers
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Client(Arc<ClientInner<'static>>);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct ClientInner<'a> {
|
|
||||||
state: Mutable<State>,
|
|
||||||
|
|
||||||
dbus_proxy: &'a DbusProxyBlocking<'a>,
|
|
||||||
|
|
||||||
primary_connection: RwLock<ObjectPath<'a>>,
|
|
||||||
primary_connection_type: RwLock<Str<'a>>,
|
|
||||||
wireless_enabled: RwLock<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
fn new() -> Result<Client> {
|
|
||||||
let state = Mutable::new(State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Unknown,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
});
|
|
||||||
let dbus_connection = Connection::system()?;
|
|
||||||
let dbus_proxy = DbusProxyBlocking::new(&dbus_connection)?;
|
|
||||||
let primary_connection = dbus_proxy.primary_connection()?.to_owned();
|
|
||||||
let primary_connection_type = dbus_proxy.primary_connection_type()?.to_owned();
|
|
||||||
let wireless_enabled = dbus_proxy.wireless_enabled()?;
|
|
||||||
|
|
||||||
Ok(Client(Arc::new(ClientInner {
|
|
||||||
state,
|
|
||||||
dbus_proxy: Box::leak(Box::new(dbus_proxy)),
|
|
||||||
primary_connection: RwLock::new(primary_connection),
|
|
||||||
primary_connection_type: RwLock::new(primary_connection_type),
|
|
||||||
wireless_enabled: RwLock::new(wireless_enabled),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(&self) -> Result<()> {
|
|
||||||
macro_rules! spawn_property_watcher {
|
|
||||||
($client:expr, $property:ident, $property_changes:ident) => {
|
|
||||||
let client = $client.clone();
|
|
||||||
spawn_blocking_result!({
|
|
||||||
while let Some(change) = client.dbus_proxy.$property_changes().next() {
|
|
||||||
{
|
|
||||||
let new_value = change.get()?;
|
|
||||||
let mut value_guard = write_lock!(client.$property);
|
|
||||||
*value_guard = new_value;
|
|
||||||
}
|
|
||||||
client.state.set(determine_state(
|
|
||||||
read_lock!(client.primary_connection).deref(),
|
|
||||||
read_lock!(client.primary_connection_type).deref(),
|
|
||||||
*read_lock!(client.wireless_enabled),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial state
|
|
||||||
self.0.state.set(determine_state(
|
|
||||||
&read_lock!(self.0.primary_connection),
|
|
||||||
&read_lock!(self.0.primary_connection_type),
|
|
||||||
*read_lock!(self.0.wireless_enabled),
|
|
||||||
));
|
|
||||||
|
|
||||||
spawn_property_watcher!(
|
|
||||||
self.0,
|
|
||||||
primary_connection,
|
|
||||||
receive_primary_connection_changed
|
|
||||||
);
|
|
||||||
spawn_property_watcher!(
|
|
||||||
self.0,
|
|
||||||
primary_connection_type,
|
|
||||||
receive_primary_connection_type_changed
|
|
||||||
);
|
|
||||||
spawn_property_watcher!(self.0, wireless_enabled, receive_wireless_enabled_changed);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscribe(&self) -> MutableSignalCloned<State> {
|
|
||||||
self.0.state.signal_cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_client() -> Result<Arc<Client>> {
|
|
||||||
let client = Arc::new(Client::new()?);
|
|
||||||
{
|
|
||||||
let client = client.clone();
|
|
||||||
spawn_blocking_result!({
|
|
||||||
client.run()?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn determine_state(
|
|
||||||
primary_connection: &ObjectPath,
|
|
||||||
primary_connection_type: &Str,
|
|
||||||
wireless_enabled: bool,
|
|
||||||
) -> State {
|
|
||||||
if primary_connection == "/" {
|
|
||||||
if wireless_enabled {
|
|
||||||
State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Disconnected,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Disabled,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match primary_connection_type.as_str() {
|
|
||||||
"802-3-ethernet" | "adsl" | "pppoe" => State {
|
|
||||||
wired: WiredState::Connected,
|
|
||||||
wifi: WifiState::Unknown,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
},
|
|
||||||
"802-11-olpc-mesh" | "802-11-wireless" | "wifi-p2p" => State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Connected(WifiConnectedState {
|
|
||||||
ssid: String::new(),
|
|
||||||
}),
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
},
|
|
||||||
"cdma" | "gsm" | "wimax" => State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Unknown,
|
|
||||||
cellular: CellularState::Connected,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
},
|
|
||||||
"vpn" | "wireguard" => State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Unknown,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Connected(VpnConnectedState {
|
|
||||||
name: String::new(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
_ => State {
|
|
||||||
wired: WiredState::Unknown,
|
|
||||||
wifi: WifiState::Unknown,
|
|
||||||
cellular: CellularState::Unknown,
|
|
||||||
vpn: VpnState::Unknown,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fn instantiate_active_connections<'a>(
|
|
||||||
// dbus_connection: &Connection,
|
|
||||||
// active_connection_paths: Vec<ObjectPath>,
|
|
||||||
// ) -> Result<Vec<ActiveConnectionDbusProxyBlocking<'a>>> {
|
|
||||||
// let mut active_connections = Vec::new();
|
|
||||||
// for active_connection_path in active_connection_paths {
|
|
||||||
// let active_connection_proxy = ActiveConnectionDbusProxyBlocking::builder(dbus_connection)
|
|
||||||
// .path(active_connection_path)?
|
|
||||||
// .build()?;
|
|
||||||
// active_connections.push(active_connection_proxy);
|
|
||||||
// }
|
|
||||||
// Ok(active_connections)
|
|
||||||
// }
|
|
||||||
|
|
||||||
register_fallible_client!(Client, networkmanager);
|
|
133
src/clients/networkmanager/dbus.rs
Normal file
133
src/clients/networkmanager/dbus.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
use zbus::dbus_proxy;
|
||||||
|
use zbus::zvariant::{ObjectPath, OwnedValue, Str};
|
||||||
|
|
||||||
|
#[dbus_proxy(
|
||||||
|
default_service = "org.freedesktop.NetworkManager",
|
||||||
|
interface = "org.freedesktop.NetworkManager",
|
||||||
|
default_path = "/org/freedesktop/NetworkManager"
|
||||||
|
)]
|
||||||
|
trait Dbus {
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn active_connections(&self) -> Result<Vec<ObjectPath>>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn devices(&self) -> Result<Vec<ObjectPath>>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn networking_enabled(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn primary_connection(&self) -> Result<ObjectPath>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn primary_connection_type(&self) -> Result<Str>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn wireless_enabled(&self) -> Result<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_proxy(
|
||||||
|
default_service = "org.freedesktop.NetworkManager",
|
||||||
|
interface = "org.freedesktop.NetworkManager.Connection.Active"
|
||||||
|
)]
|
||||||
|
trait ActiveConnectionDbus {
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn connection(&self) -> Result<ObjectPath>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn default(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn default6(&self) -> Result<bool>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn devices(&self) -> Result<Vec<ObjectPath>>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn id(&self) -> Result<Str>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn type_(&self) -> Result<Str>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn uuid(&self) -> Result<Str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[dbus_proxy(
|
||||||
|
default_service = "org.freedesktop.NetworkManager",
|
||||||
|
interface = "org.freedesktop.NetworkManager.Device"
|
||||||
|
)]
|
||||||
|
trait DeviceDbus {
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn active_connection(&self) -> Result<ObjectPath>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn device_type(&self) -> Result<DeviceType>;
|
||||||
|
|
||||||
|
#[dbus_proxy(property)]
|
||||||
|
fn state(&self) -> Result<DeviceState>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, OwnedValue, PartialEq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub(super) enum DeviceType {
|
||||||
|
Unknown = 0,
|
||||||
|
Ethernet = 1,
|
||||||
|
Wifi = 2,
|
||||||
|
Bluetooth = 5,
|
||||||
|
OlpcMesh = 6,
|
||||||
|
Wimax = 7,
|
||||||
|
Modem = 8,
|
||||||
|
Infiniband = 9,
|
||||||
|
Bond = 10,
|
||||||
|
Vlan = 11,
|
||||||
|
Adsl = 12,
|
||||||
|
Bridge = 13,
|
||||||
|
Team = 15,
|
||||||
|
Tun = 16,
|
||||||
|
IpTunnel = 17,
|
||||||
|
Macvlan = 18,
|
||||||
|
Vxlan = 19,
|
||||||
|
Veth = 20,
|
||||||
|
Macsec = 21,
|
||||||
|
Dummy = 22,
|
||||||
|
Ppp = 23,
|
||||||
|
OvsInterface = 24,
|
||||||
|
OvsPort = 25,
|
||||||
|
OvsBridge = 26,
|
||||||
|
Wpan = 27,
|
||||||
|
Lowpan = 28,
|
||||||
|
Wireguard = 29,
|
||||||
|
WifiP2p = 30,
|
||||||
|
Vrf = 31,
|
||||||
|
Loopback = 32,
|
||||||
|
Hsr = 33,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, OwnedValue, PartialEq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub(super) enum DeviceState {
|
||||||
|
Unknown = 0,
|
||||||
|
Unmanaged = 10,
|
||||||
|
Unavailable = 20,
|
||||||
|
Disconnected = 30,
|
||||||
|
Prepare = 40,
|
||||||
|
Config = 50,
|
||||||
|
NeedAuth = 60,
|
||||||
|
IpConfig = 70,
|
||||||
|
IpCheck = 80,
|
||||||
|
Secondaries = 90,
|
||||||
|
Activated = 100,
|
||||||
|
Deactivating = 110,
|
||||||
|
Failed = 120,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceState {
|
||||||
|
pub(super) fn is_enabled(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
DeviceState::Unknown | DeviceState::Unmanaged | DeviceState::Unavailable => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
src/clients/networkmanager/mod.rs
Normal file
217
src/clients/networkmanager/mod.rs
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
mod dbus;
|
||||||
|
pub mod state;
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
use color_eyre::Result;
|
||||||
|
use futures_signals::signal::{Mutable, MutableSignalCloned};
|
||||||
|
use tracing::error;
|
||||||
|
use zbus::blocking::Connection;
|
||||||
|
use zbus::zvariant::ObjectPath;
|
||||||
|
|
||||||
|
use crate::clients::networkmanager::dbus::{
|
||||||
|
ActiveConnectionDbusProxyBlocking, DbusProxyBlocking, DeviceDbusProxyBlocking,
|
||||||
|
};
|
||||||
|
use crate::clients::networkmanager::state::{
|
||||||
|
determine_cellular_state, determine_vpn_state, determine_wifi_state, determine_wired_state,
|
||||||
|
CellularState, State, VpnState, WifiState, WiredState,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
read_lock, register_fallible_client, spawn_blocking, spawn_blocking_result, write_lock,
|
||||||
|
};
|
||||||
|
|
||||||
|
type PathMap<'a, T> = HashMap<ObjectPath<'a>, T>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client(Arc<ClientInner<'static>>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ClientInner<'a> {
|
||||||
|
state: Mutable<State>,
|
||||||
|
root_object: &'a DbusProxyBlocking<'a>,
|
||||||
|
active_connections: RwLock<PathMap<'a, ActiveConnectionDbusProxyBlocking<'a>>>,
|
||||||
|
devices: RwLock<PathMap<'a, DeviceDbusProxyBlocking<'a>>>,
|
||||||
|
dbus_connection: Connection,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
fn new() -> Result<Client> {
|
||||||
|
let state = Mutable::new(State {
|
||||||
|
wired: WiredState::Unknown,
|
||||||
|
wifi: WifiState::Unknown,
|
||||||
|
cellular: CellularState::Unknown,
|
||||||
|
vpn: VpnState::Unknown,
|
||||||
|
});
|
||||||
|
let dbus_connection = Connection::system()?;
|
||||||
|
let dbus_proxy = DbusProxyBlocking::new(&dbus_connection)?;
|
||||||
|
|
||||||
|
Ok(Client(Arc::new(ClientInner {
|
||||||
|
state,
|
||||||
|
root_object: Box::leak(Box::new(dbus_proxy)), // TODO: Check if boxing is still necessary
|
||||||
|
active_connections: RwLock::new(HashMap::new()),
|
||||||
|
devices: RwLock::new(HashMap::new()),
|
||||||
|
dbus_connection,
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self) -> Result<()> {
|
||||||
|
macro_rules! spawn_property_watcher {
|
||||||
|
($client:expr, $property_changes:ident) => {
|
||||||
|
let client = $client.clone();
|
||||||
|
spawn_blocking_result!({
|
||||||
|
while let Some(_) = client.root_object.$property_changes().next() {
|
||||||
|
client.state.set(State {
|
||||||
|
wired: determine_wired_state(&read_lock!(client.devices))?,
|
||||||
|
wifi: determine_wifi_state(&read_lock!(client.devices))?,
|
||||||
|
cellular: determine_cellular_state(&read_lock!(client.devices))?,
|
||||||
|
vpn: determine_vpn_state(&read_lock!(client.active_connections))?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! spawn_list_watcher {
|
||||||
|
() => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial state
|
||||||
|
self.0.state.set(State {
|
||||||
|
wired: determine_wired_state(&read_lock!(self.0.devices))?,
|
||||||
|
wifi: determine_wifi_state(&read_lock!(self.0.devices))?,
|
||||||
|
cellular: determine_cellular_state(&read_lock!(self.0.devices))?,
|
||||||
|
vpn: determine_vpn_state(&read_lock!(self.0.active_connections))?,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Active connections paths list watcher
|
||||||
|
{
|
||||||
|
let client = self.0.clone();
|
||||||
|
spawn_blocking_result!({
|
||||||
|
let mut changes = client.root_object.receive_active_connections_changed();
|
||||||
|
while let Some(_) = changes.next() {
|
||||||
|
let mut new_pathmap = HashMap::new();
|
||||||
|
{
|
||||||
|
let new_paths = client.root_object.active_connections()?;
|
||||||
|
let active_connections = read_lock!(client.active_connections);
|
||||||
|
for new_path in new_paths {
|
||||||
|
if active_connections.contains_key(&new_path) {
|
||||||
|
let proxy = active_connections
|
||||||
|
.get(&new_path)
|
||||||
|
.expect("Should contain the key, see check above");
|
||||||
|
new_pathmap.insert(new_path, proxy.clone());
|
||||||
|
} else {
|
||||||
|
let new_proxy = ActiveConnectionDbusProxyBlocking::builder(
|
||||||
|
&client.dbus_connection,
|
||||||
|
)
|
||||||
|
.path(new_path.clone())?
|
||||||
|
.build()?;
|
||||||
|
new_pathmap.insert(new_path, new_proxy);
|
||||||
|
|
||||||
|
// Active connection type is assumed to never change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*write_lock!(client.active_connections) = new_pathmap;
|
||||||
|
client.state.set(State {
|
||||||
|
wired: determine_wired_state(&read_lock!(client.devices))?,
|
||||||
|
wifi: determine_wifi_state(&read_lock!(client.devices))?,
|
||||||
|
cellular: determine_cellular_state(&read_lock!(client.devices))?,
|
||||||
|
vpn: determine_vpn_state(&read_lock!(client.active_connections))?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devices paths list watcher
|
||||||
|
{
|
||||||
|
let client = self.0.clone();
|
||||||
|
spawn_blocking_result!({
|
||||||
|
let mut changes = client.root_object.receive_devices_changed();
|
||||||
|
while let Some(_) = changes.next() {
|
||||||
|
let mut new_pathmap = HashMap::new();
|
||||||
|
{
|
||||||
|
let new_paths = client.root_object.devices()?;
|
||||||
|
let devices = read_lock!(client.devices);
|
||||||
|
for new_path in new_paths {
|
||||||
|
if devices.contains_key(&new_path) {
|
||||||
|
let proxy = devices
|
||||||
|
.get(&new_path)
|
||||||
|
.expect("Should contain the key, see check above");
|
||||||
|
new_pathmap.insert(new_path, proxy.clone());
|
||||||
|
} else {
|
||||||
|
let new_proxy =
|
||||||
|
DeviceDbusProxyBlocking::builder(&client.dbus_connection)
|
||||||
|
.path(new_path.clone())?
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
// Specific device state watcher
|
||||||
|
{
|
||||||
|
let client = client.clone();
|
||||||
|
let new_path = new_path.clone();
|
||||||
|
spawn_blocking_result!({
|
||||||
|
let mut changes = read_lock!(client.devices)
|
||||||
|
.get(&new_path)
|
||||||
|
.unwrap()
|
||||||
|
.receive_state_changed();
|
||||||
|
while let Some(_) = changes.next() {
|
||||||
|
client.state.set(State {
|
||||||
|
wired: determine_wired_state(&read_lock!(
|
||||||
|
client.devices
|
||||||
|
))?,
|
||||||
|
wifi: determine_wifi_state(&read_lock!(
|
||||||
|
client.devices
|
||||||
|
))?,
|
||||||
|
cellular: determine_cellular_state(&read_lock!(
|
||||||
|
client.devices
|
||||||
|
))?,
|
||||||
|
vpn: determine_vpn_state(&read_lock!(
|
||||||
|
client.active_connections
|
||||||
|
))?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device type is assumed to never change
|
||||||
|
|
||||||
|
new_pathmap.insert(new_path, new_proxy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*write_lock!(client.devices) = new_pathmap;
|
||||||
|
client.state.set(State {
|
||||||
|
wired: determine_wired_state(&read_lock!(client.devices))?,
|
||||||
|
wifi: determine_wifi_state(&read_lock!(client.devices))?,
|
||||||
|
cellular: determine_cellular_state(&read_lock!(client.devices))?,
|
||||||
|
vpn: determine_vpn_state(&read_lock!(client.active_connections))?,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe(&self) -> MutableSignalCloned<State> {
|
||||||
|
self.0.state.signal_cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_client() -> Result<Arc<Client>> {
|
||||||
|
let client = Arc::new(Client::new()?);
|
||||||
|
{
|
||||||
|
let client = client.clone();
|
||||||
|
spawn_blocking_result!({
|
||||||
|
client.run()?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
register_fallible_client!(Client, networkmanager);
|
163
src/clients/networkmanager/state.rs
Normal file
163
src/clients/networkmanager/state.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
|
||||||
|
use crate::clients::networkmanager::dbus::{
|
||||||
|
ActiveConnectionDbusProxyBlocking, DeviceDbusProxyBlocking, DeviceState, DeviceType,
|
||||||
|
};
|
||||||
|
use crate::clients::networkmanager::PathMap;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct State {
|
||||||
|
pub wired: WiredState,
|
||||||
|
pub wifi: WifiState,
|
||||||
|
pub cellular: CellularState,
|
||||||
|
pub vpn: VpnState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum WiredState {
|
||||||
|
Connected,
|
||||||
|
Disconnected,
|
||||||
|
NotPresent,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum WifiState {
|
||||||
|
Connected(WifiConnectedState),
|
||||||
|
Disconnected,
|
||||||
|
Disabled,
|
||||||
|
NotPresent,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct WifiConnectedState {
|
||||||
|
pub ssid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum CellularState {
|
||||||
|
Connected,
|
||||||
|
Disconnected,
|
||||||
|
Disabled,
|
||||||
|
NotPresent,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum VpnState {
|
||||||
|
Connected(VpnConnectedState),
|
||||||
|
Disconnected,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct VpnConnectedState {
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn determine_wired_state(
|
||||||
|
devices: &PathMap<DeviceDbusProxyBlocking>,
|
||||||
|
) -> Result<WiredState> {
|
||||||
|
let mut present = false;
|
||||||
|
let mut connected = false;
|
||||||
|
|
||||||
|
for device in devices.values() {
|
||||||
|
if device.device_type()? == DeviceType::Ethernet {
|
||||||
|
present = true;
|
||||||
|
if device.state()?.is_enabled() {
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if connected {
|
||||||
|
Ok(WiredState::Connected)
|
||||||
|
} else if present {
|
||||||
|
Ok(WiredState::Disconnected)
|
||||||
|
} else {
|
||||||
|
Ok(WiredState::NotPresent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn determine_wifi_state(
|
||||||
|
devices: &PathMap<DeviceDbusProxyBlocking>,
|
||||||
|
) -> Result<WifiState> {
|
||||||
|
let mut present = false;
|
||||||
|
let mut enabled = false;
|
||||||
|
let mut connected = false;
|
||||||
|
|
||||||
|
for device in devices.values() {
|
||||||
|
if device.device_type()? == DeviceType::Wifi {
|
||||||
|
present = true;
|
||||||
|
if device.state()?.is_enabled() {
|
||||||
|
enabled = true;
|
||||||
|
if device.state()? == DeviceState::Activated {
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if connected {
|
||||||
|
Ok(WifiState::Connected(WifiConnectedState {
|
||||||
|
// TODO: Implement obtaining SSID
|
||||||
|
ssid: "unknown".into(),
|
||||||
|
}))
|
||||||
|
} else if enabled {
|
||||||
|
Ok(WifiState::Disconnected)
|
||||||
|
} else if present {
|
||||||
|
Ok(WifiState::Disabled)
|
||||||
|
} else {
|
||||||
|
Ok(WifiState::NotPresent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn determine_cellular_state(
|
||||||
|
devices: &PathMap<DeviceDbusProxyBlocking>,
|
||||||
|
) -> Result<CellularState> {
|
||||||
|
let mut present = false;
|
||||||
|
let mut enabled = false;
|
||||||
|
let mut connected = false;
|
||||||
|
|
||||||
|
for device in devices.values() {
|
||||||
|
if device.device_type()? == DeviceType::Modem {
|
||||||
|
present = true;
|
||||||
|
if device.state()?.is_enabled() {
|
||||||
|
enabled = true;
|
||||||
|
if device.state()? == DeviceState::Activated {
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if connected {
|
||||||
|
Ok(CellularState::Connected)
|
||||||
|
} else if enabled {
|
||||||
|
Ok(CellularState::Disconnected)
|
||||||
|
} else if present {
|
||||||
|
Ok(CellularState::Disabled)
|
||||||
|
} else {
|
||||||
|
Ok(CellularState::NotPresent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn determine_vpn_state(
|
||||||
|
active_connections: &PathMap<ActiveConnectionDbusProxyBlocking>,
|
||||||
|
) -> Result<VpnState> {
|
||||||
|
for connection in active_connections.values() {
|
||||||
|
match connection.type_()?.as_str() {
|
||||||
|
"vpn" | "wireguard" => {
|
||||||
|
return Ok(VpnState::Connected(VpnConnectedState {
|
||||||
|
name: "unknown".into(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(VpnState::Disconnected)
|
||||||
|
}
|
|
@ -6,7 +6,10 @@ use gtk::{Box as GtkBox, Image, Orientation};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use tokio::sync::mpsc::Receiver;
|
||||||
|
|
||||||
use crate::clients::networkmanager::{CellularState, Client, State, VpnState, WifiState, WiredState};
|
use crate::clients::networkmanager::state::{
|
||||||
|
CellularState, State, VpnState, WifiState, WiredState,
|
||||||
|
};
|
||||||
|
use crate::clients::networkmanager::Client;
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::gtk_helpers::IronbarGtkExt;
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
use crate::image::ImageProvider;
|
use crate::image::ImageProvider;
|
||||||
|
@ -59,28 +62,36 @@ impl Module<GtkBox> for NetworkManagerModule {
|
||||||
|
|
||||||
// Wired icon
|
// Wired icon
|
||||||
let wired_icon = Image::new();
|
let wired_icon = Image::new();
|
||||||
|
wired_icon.add_class("icon");
|
||||||
wired_icon.add_class("wired-icon");
|
wired_icon.add_class("wired-icon");
|
||||||
container.add(&wired_icon);
|
container.add(&wired_icon);
|
||||||
|
|
||||||
// Wifi icon
|
// Wifi icon
|
||||||
let wifi_icon = Image::new();
|
let wifi_icon = Image::new();
|
||||||
|
wifi_icon.add_class("icon");
|
||||||
wifi_icon.add_class("wifi-icon");
|
wifi_icon.add_class("wifi-icon");
|
||||||
container.add(&wifi_icon);
|
container.add(&wifi_icon);
|
||||||
|
|
||||||
// Cellular icon
|
// Cellular icon
|
||||||
let cellular_icon = Image::new();
|
let cellular_icon = Image::new();
|
||||||
|
cellular_icon.add_class("icon");
|
||||||
cellular_icon.add_class("cellular-icon");
|
cellular_icon.add_class("cellular-icon");
|
||||||
container.add(&cellular_icon);
|
container.add(&cellular_icon);
|
||||||
|
|
||||||
// VPN icon
|
// VPN icon
|
||||||
let vpn_icon = Image::new();
|
let vpn_icon = Image::new();
|
||||||
|
vpn_icon.add_class("icon");
|
||||||
vpn_icon.add_class("vpn-icon");
|
vpn_icon.add_class("vpn-icon");
|
||||||
container.add(&vpn_icon);
|
container.add(&vpn_icon);
|
||||||
|
|
||||||
let icon_theme = info.icon_theme.clone();
|
let icon_theme = info.icon_theme.clone();
|
||||||
glib_recv!(context.subscribe(), state => {
|
glib_recv!(context.subscribe(), state => {
|
||||||
macro_rules! update_icon {
|
macro_rules! update_icon {
|
||||||
($icon_var:expr, $state_type:ident, {$($state:pat => $icon_name:expr,)+}) => {
|
(
|
||||||
|
$icon_var:expr,
|
||||||
|
$state_type:ident,
|
||||||
|
{$($state:pat => $icon_name:expr,)+}
|
||||||
|
) => {
|
||||||
let icon_name = match state.$state_type {
|
let icon_name = match state.$state_type {
|
||||||
$($state => $icon_name,)+
|
$($state => $icon_name,)+
|
||||||
};
|
};
|
||||||
|
@ -113,7 +124,8 @@ impl Module<GtkBox> for NetworkManagerModule {
|
||||||
});
|
});
|
||||||
update_icon!(vpn_icon, vpn, {
|
update_icon!(vpn_icon, vpn, {
|
||||||
VpnState::Connected(_) => "icon:network-vpn-symbolic",
|
VpnState::Connected(_) => "icon:network-vpn-symbolic",
|
||||||
VpnState::Disconnected | VpnState::Unknown => "",
|
VpnState::Disconnected => "icon:network-vpn-disabled-symbolic",
|
||||||
|
VpnState::Unknown => "",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue