mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-04-19 11:24: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 |
|
||||
|------------------------|----------------------------------|
|
||||
| `.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).
|
||||
|
|
|
@ -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 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::gtk_helpers::IronbarGtkExt;
|
||||
use crate::image::ImageProvider;
|
||||
|
@ -59,28 +62,36 @@ impl Module<GtkBox> for NetworkManagerModule {
|
|||
|
||||
// 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,)+}) => {
|
||||
(
|
||||
$icon_var:expr,
|
||||
$state_type:ident,
|
||||
{$($state:pat => $icon_name:expr,)+}
|
||||
) => {
|
||||
let icon_name = match state.$state_type {
|
||||
$($state => $icon_name,)+
|
||||
};
|
||||
|
@ -113,7 +124,8 @@ impl Module<GtkBox> for NetworkManagerModule {
|
|||
});
|
||||
update_icon!(vpn_icon, vpn, {
|
||||
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