mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2026-01-11 21:46:44 +01:00
Compare commits
48 commits
feat/volum
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
54e91facd3 |
|||
|
d1f433c990 |
|||
|
f4369b20d3 |
|||
|
92a1b3455d |
|||
|
c7e002ee6c |
|||
|
a2e72bcdde |
|||
|
ef26554434 |
|||
|
|
ca77a991f7 |
||
|
|
279ef938d1 |
||
|
24ef1b700a |
|||
|
b7557bc78b |
|||
|
037de421cd |
|||
|
|
4041e080d0 |
||
|
|
491e055aa1 |
||
|
|
304b1d3e17 |
||
|
|
0159c0155e | ||
|
dea571506f |
|||
|
|
bd5745f2ef | ||
|
106e23e311 |
|||
|
546e75c43c |
|||
|
50ab48500f |
|||
|
798a71cc13 |
|||
|
|
dca0e07ce2 |
||
|
|
8c9673eb1f |
||
|
a92e6684a0 |
|||
|
|
eadc7a7bab |
||
|
|
c3c3dab11b |
||
|
|
dd9a0c2065 |
||
|
|
5520562a18 |
||
|
233f7f1ee2 |
|||
|
feccc29fd1 |
|||
|
ccb57c8458 |
|||
|
|
ff185d0de9 | ||
|
34da5edfe0 |
|||
|
622ca3cc4f |
|||
|
40adfaf810 |
|||
|
749b9f0433 |
|||
|
c4f5485d53 |
|||
|
a67e722dee |
|||
|
f1a8b42cfa |
|||
|
3a3888a2d6 |
|||
|
b68e4b4af9 |
|||
|
f1dc35e873 |
|||
|
dc8af7ada8 |
|||
|
613495ce0b |
|||
|
|
98e9af68d9 |
||
|
|
1fbe655480 |
||
|
|
93a797be3f |
8 changed files with 346 additions and 298 deletions
76
Cargo.lock
generated
76
Cargo.lock
generated
|
|
@ -736,12 +736,6 @@ dependencies = [
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "discard"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|
@ -1018,22 +1012,6 @@ dependencies = [
|
||||||
"syn 2.0.99",
|
"syn 2.0.99",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-signals"
|
|
||||||
version = "0.3.34"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "70abe9c40a0dccd69bf7c59ba58714ebeb6c15a88143a10c6be7130e895f1696"
|
|
||||||
dependencies = [
|
|
||||||
"discard",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"gensym",
|
|
||||||
"log",
|
|
||||||
"pin-project",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
|
|
@ -1145,18 +1123,6 @@ dependencies = [
|
||||||
"version_check",
|
"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.39",
|
|
||||||
"syn 2.0.99",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
|
@ -1845,7 +1811,6 @@ dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"evdev-rs",
|
"evdev-rs",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"futures-signals",
|
|
||||||
"glib",
|
"glib",
|
||||||
"gtk",
|
"gtk",
|
||||||
"gtk-layer-shell",
|
"gtk-layer-shell",
|
||||||
|
|
@ -1869,6 +1834,7 @@ dependencies = [
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"system-tray",
|
"system-tray",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
|
|
@ -2541,26 +2507,6 @@ dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project"
|
|
||||||
version = "1.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-internal",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-internal"
|
|
||||||
version = "1.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote 1.0.39",
|
|
||||||
"syn 2.0.99",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
|
@ -3557,6 +3503,17 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.13"
|
version = "0.7.13"
|
||||||
|
|
@ -3865,15 +3822,6 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "1.15.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ music = ["dep:regex"]
|
||||||
"music+mpris" = ["music", "mpris"]
|
"music+mpris" = ["music", "mpris"]
|
||||||
"music+mpd" = ["music", "mpd-utils"]
|
"music+mpd" = ["music", "mpd-utils"]
|
||||||
|
|
||||||
network_manager = ["futures-lite", "futures-signals", "zbus"]
|
network_manager = ["futures-lite", "tokio-stream", "zbus"]
|
||||||
|
|
||||||
notifications = ["zbus"]
|
notifications = ["zbus"]
|
||||||
|
|
||||||
|
|
@ -171,7 +171,7 @@ regex = { version = "1.11.1", default-features = false, features = [
|
||||||
], optional = true }
|
], optional = true }
|
||||||
|
|
||||||
# network_manager
|
# network_manager
|
||||||
futures-signals = { version = "0.3.34", optional = true }
|
tokio-stream = { version = "0.1.17", optional = true }
|
||||||
|
|
||||||
# sys_info
|
# sys_info
|
||||||
sysinfo = { version = "0.36.1", optional = true }
|
sysinfo = { version = "0.36.1", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ impl Clients {
|
||||||
if let Some(client) = &self.network_manager {
|
if let Some(client) = &self.network_manager {
|
||||||
Ok(client.clone())
|
Ok(client.clone())
|
||||||
} else {
|
} else {
|
||||||
let client = await_sync(async move { networkmanager::create_client().await })?;
|
let client = networkmanager::create_client()?;
|
||||||
self.network_manager = Some(client.clone());
|
self.network_manager = Some(client.clone());
|
||||||
Ok(client)
|
Ok(client)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{register_fallible_client, spawn};
|
|
||||||
use color_eyre::Result;
|
|
||||||
use futures_signals::signal::{Mutable, MutableSignalCloned};
|
|
||||||
use tracing::error;
|
|
||||||
use zbus::export::ordered_stream::OrderedStreamExt;
|
|
||||||
use zbus::fdo::PropertiesProxy;
|
|
||||||
use zbus::{
|
|
||||||
Connection,
|
|
||||||
names::InterfaceName,
|
|
||||||
proxy,
|
|
||||||
zvariant::{ObjectPath, Str},
|
|
||||||
};
|
|
||||||
|
|
||||||
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<ClientState>,
|
|
||||||
interface_name: InterfaceName<'static>,
|
|
||||||
dbus_connection: Connection,
|
|
||||||
props_proxy: PropertiesProxy<'static>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum ClientState {
|
|
||||||
WiredConnected,
|
|
||||||
WifiConnected,
|
|
||||||
CellularConnected,
|
|
||||||
VpnConnected,
|
|
||||||
WifiDisconnected,
|
|
||||||
Offline,
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proxy(
|
|
||||||
default_service = "org.freedesktop.NetworkManager",
|
|
||||||
interface = "org.freedesktop.NetworkManager",
|
|
||||||
default_path = "/org/freedesktop/NetworkManager"
|
|
||||||
)]
|
|
||||||
trait NetworkManagerDbus {
|
|
||||||
#[zbus(property)]
|
|
||||||
fn active_connections(&self) -> Result<Vec<ObjectPath>>;
|
|
||||||
|
|
||||||
#[zbus(property)]
|
|
||||||
fn devices(&self) -> Result<Vec<ObjectPath>>;
|
|
||||||
|
|
||||||
#[zbus(property)]
|
|
||||||
fn networking_enabled(&self) -> Result<bool>;
|
|
||||||
|
|
||||||
#[zbus(property)]
|
|
||||||
fn primary_connection(&self) -> Result<ObjectPath>;
|
|
||||||
|
|
||||||
#[zbus(property)]
|
|
||||||
fn primary_connection_type(&self) -> Result<Str>;
|
|
||||||
|
|
||||||
#[zbus(property)]
|
|
||||||
fn wireless_enabled(&self) -> Result<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Client {
|
|
||||||
async fn new() -> Result<Self> {
|
|
||||||
let client_state = Mutable::new(ClientState::Unknown);
|
|
||||||
let dbus_connection = Connection::system().await?;
|
|
||||||
let interface_name = InterfaceName::from_static_str(DBUS_INTERFACE)?;
|
|
||||||
let props_proxy = PropertiesProxy::builder(&dbus_connection)
|
|
||||||
.destination(DBUS_BUS)?
|
|
||||||
.path(DBUS_PATH)?
|
|
||||||
.build()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
client_state,
|
|
||||||
interface_name,
|
|
||||||
dbus_connection,
|
|
||||||
props_proxy,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(&self) -> Result<()> {
|
|
||||||
let proxy = NetworkManagerDbusProxy::new(&self.dbus_connection).await?;
|
|
||||||
|
|
||||||
let mut primary_connection = proxy.primary_connection().await?;
|
|
||||||
let mut primary_connection_type = proxy.primary_connection_type().await?;
|
|
||||||
let mut wireless_enabled = proxy.wireless_enabled().await?;
|
|
||||||
|
|
||||||
self.client_state.set(determine_state(
|
|
||||||
&primary_connection,
|
|
||||||
&primary_connection_type,
|
|
||||||
wireless_enabled,
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut stream = self.props_proxy.receive_properties_changed().await?;
|
|
||||||
while let Some(change) = stream.next().await {
|
|
||||||
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().await?;
|
|
||||||
relevant_prop_changed = true;
|
|
||||||
}
|
|
||||||
if changed_props.contains_key("PrimaryConnectionType") {
|
|
||||||
primary_connection_type = proxy.primary_connection_type().await?;
|
|
||||||
relevant_prop_changed = true;
|
|
||||||
}
|
|
||||||
if changed_props.contains_key("WirelessEnabled") {
|
|
||||||
wireless_enabled = proxy.wireless_enabled().await?;
|
|
||||||
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<ClientState> {
|
|
||||||
self.client_state.signal_cloned()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_client() -> Result<Arc<Client>> {
|
|
||||||
let client = Arc::new(Client::new().await?);
|
|
||||||
{
|
|
||||||
let client = client.clone();
|
|
||||||
spawn(async move {
|
|
||||||
if let Err(error) = client.run().await {
|
|
||||||
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);
|
|
||||||
84
src/clients/networkmanager/dbus.rs
Normal file
84
src/clients/networkmanager/dbus.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
use zbus::proxy;
|
||||||
|
use zbus::zvariant::{ObjectPath, OwnedValue, Str};
|
||||||
|
|
||||||
|
#[proxy(
|
||||||
|
default_service = "org.freedesktop.NetworkManager",
|
||||||
|
interface = "org.freedesktop.NetworkManager",
|
||||||
|
default_path = "/org/freedesktop/NetworkManager"
|
||||||
|
)]
|
||||||
|
pub(super) trait Dbus {
|
||||||
|
#[zbus(property)]
|
||||||
|
fn all_devices(&self) -> Result<Vec<ObjectPath<'_>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proxy(
|
||||||
|
default_service = "org.freedesktop.NetworkManager",
|
||||||
|
interface = "org.freedesktop.NetworkManager.Device"
|
||||||
|
)]
|
||||||
|
pub(super) trait DeviceDbus {
|
||||||
|
#[zbus(property)]
|
||||||
|
fn device_type(&self) -> Result<DeviceType>;
|
||||||
|
|
||||||
|
#[zbus(property)]
|
||||||
|
fn interface(&self) -> Result<Str<'_>>;
|
||||||
|
|
||||||
|
#[zbus(property)]
|
||||||
|
fn state(&self) -> Result<DeviceState>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For reference: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/e1a7d5ac062f4f23ce3a6b33c62e856056161ad8/src/libnm-core-public/nm-dbus-interface.h#L212-L253
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, OwnedValue, PartialEq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// For reference: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/blob/e1a7d5ac062f4f23ce3a6b33c62e856056161ad8/src/libnm-core-public/nm-dbus-interface.h#L501-L538
|
||||||
|
#[derive(Clone, Debug, OwnedValue, PartialEq)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub 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,
|
||||||
|
}
|
||||||
13
src/clients/networkmanager/event.rs
Normal file
13
src/clients/networkmanager/event.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
use crate::clients::networkmanager::dbus::{DeviceState, DeviceType};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Event {
|
||||||
|
DeviceAdded {
|
||||||
|
interface: String,
|
||||||
|
},
|
||||||
|
DeviceStateChanged {
|
||||||
|
interface: String,
|
||||||
|
r#type: DeviceType,
|
||||||
|
state: DeviceState,
|
||||||
|
},
|
||||||
|
}
|
||||||
118
src/clients/networkmanager/mod.rs
Normal file
118
src/clients/networkmanager/mod.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
use color_eyre::eyre::Ok;
|
||||||
|
use futures_lite::StreamExt;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::broadcast;
|
||||||
|
use zbus::Connection;
|
||||||
|
use zbus::zvariant::{ObjectPath, Str};
|
||||||
|
|
||||||
|
use crate::clients::ClientResult;
|
||||||
|
use crate::clients::networkmanager::dbus::{DbusProxy, DeviceDbusProxy};
|
||||||
|
use crate::clients::networkmanager::event::Event;
|
||||||
|
use crate::{register_fallible_client, spawn};
|
||||||
|
|
||||||
|
pub mod dbus;
|
||||||
|
pub mod event;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Client {
|
||||||
|
tx: broadcast::Sender<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
fn new() -> Result<Client> {
|
||||||
|
let (tx, _) = broadcast::channel(64);
|
||||||
|
Ok(Client { tx })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&self) -> Result<()> {
|
||||||
|
let tx = self.tx.clone();
|
||||||
|
spawn(async move {
|
||||||
|
let dbus_connection = Connection::system().await?;
|
||||||
|
let root = DbusProxy::new(&dbus_connection).await?;
|
||||||
|
|
||||||
|
let mut devices = HashSet::new();
|
||||||
|
|
||||||
|
let mut devices_changes = root.receive_all_devices_changed().await;
|
||||||
|
while let Some(devices_change) = devices_changes.next().await {
|
||||||
|
// The new list of devices from dbus, not to be confused with the added devices below
|
||||||
|
let new_devices = HashSet::from_iter(devices_change.get().await?);
|
||||||
|
|
||||||
|
let added_devices = new_devices.difference(&devices);
|
||||||
|
for added_device in added_devices {
|
||||||
|
spawn(watch_device(added_device.to_owned(), tx.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let _removed_devices = devices.difference(&new_devices);
|
||||||
|
// TODO: Cook up some way to notify closures for removed devices to exit
|
||||||
|
|
||||||
|
devices = new_devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe(&self) -> broadcast::Receiver<Event> {
|
||||||
|
self.tx.subscribe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_client() -> ClientResult<Client> {
|
||||||
|
let client = Arc::new(Client::new()?);
|
||||||
|
client.run()?;
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn watch_device(device_path: ObjectPath<'_>, tx: broadcast::Sender<Event>) -> Result<()> {
|
||||||
|
let dbus_connection = Connection::system().await?;
|
||||||
|
let device = DeviceDbusProxy::new(&dbus_connection, device_path.to_owned()).await?;
|
||||||
|
|
||||||
|
let interface = device.interface().await?;
|
||||||
|
tx.send(Event::DeviceAdded {
|
||||||
|
interface: interface.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
spawn(watch_device_state(
|
||||||
|
device_path.to_owned(),
|
||||||
|
interface.to_owned(),
|
||||||
|
tx.clone(),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn watch_device_state(
|
||||||
|
device_path: ObjectPath<'_>,
|
||||||
|
interface: Str<'_>,
|
||||||
|
tx: broadcast::Sender<Event>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let dbus_connection = Connection::system().await?;
|
||||||
|
let device = DeviceDbusProxy::new(&dbus_connection, &device_path).await?;
|
||||||
|
let r#type = device.device_type().await?;
|
||||||
|
|
||||||
|
// Send an event communicating the initial state
|
||||||
|
let state = device.state().await?;
|
||||||
|
tx.send(Event::DeviceStateChanged {
|
||||||
|
interface: interface.to_string(),
|
||||||
|
r#type: r#type.clone(),
|
||||||
|
state,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut state_changes = device.receive_state_changed().await;
|
||||||
|
while let Some(state_change) = state_changes.next().await {
|
||||||
|
let state = state_change.get().await?;
|
||||||
|
tx.send(Event::DeviceStateChanged {
|
||||||
|
interface: interface.to_string(),
|
||||||
|
r#type: r#type.clone(),
|
||||||
|
state,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
register_fallible_client!(Client, network_manager);
|
||||||
|
|
@ -1,16 +1,18 @@
|
||||||
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
|
use crate::clients::networkmanager::Client;
|
||||||
use crate::clients::networkmanager::{Client, ClientState};
|
use crate::clients::networkmanager::dbus::{DeviceState, DeviceType};
|
||||||
|
use crate::clients::networkmanager::event::Event;
|
||||||
use crate::config::CommonConfig;
|
use crate::config::CommonConfig;
|
||||||
use crate::gtk_helpers::IronbarGtkExt;
|
use crate::gtk_helpers::IronbarGtkExt;
|
||||||
|
use crate::image::Provider;
|
||||||
use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
|
use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
|
||||||
use crate::{module_impl, spawn};
|
use crate::{module_impl, spawn};
|
||||||
use color_eyre::Result;
|
use color_eyre::{Result, eyre::Ok};
|
||||||
use futures_lite::StreamExt;
|
use glib::spawn_future_local;
|
||||||
use futures_signals::signal::SignalExt;
|
use gtk::prelude::{ContainerExt, WidgetExt};
|
||||||
use gtk::prelude::ContainerExt;
|
use gtk::{Image, Orientation};
|
||||||
use gtk::{Box as GtkBox, Image};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use tokio::sync::mpsc::Receiver;
|
use std::collections::HashMap;
|
||||||
|
use tokio::sync::{broadcast, mpsc};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||||
|
|
@ -26,26 +28,29 @@ const fn default_icon_size() -> i32 {
|
||||||
24
|
24
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module<GtkBox> for NetworkManagerModule {
|
impl Module<gtk::Box> for NetworkManagerModule {
|
||||||
type SendMessage = ClientState;
|
type SendMessage = Event;
|
||||||
type ReceiveMessage = ();
|
type ReceiveMessage = ();
|
||||||
|
|
||||||
module_impl!("network_manager");
|
module_impl!("network_manager");
|
||||||
|
|
||||||
fn spawn_controller(
|
fn spawn_controller(
|
||||||
&self,
|
&self,
|
||||||
_: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
context: &WidgetContext<ClientState, ()>,
|
context: &WidgetContext<Event, ()>,
|
||||||
_: Receiver<()>,
|
_rx: mpsc::Receiver<()>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let client = context.try_client::<Client>()?;
|
let client = context.try_client::<Client>()?;
|
||||||
let mut client_signal = client.subscribe().to_stream();
|
// Should we be using context.tx with ModuleUpdateEvent::Update instead?
|
||||||
let tx = context.tx.clone();
|
let tx = context.update_tx.clone();
|
||||||
|
// Must be done here synchronously to avoid race condition
|
||||||
|
let mut client_rx = client.subscribe();
|
||||||
spawn(async move {
|
spawn(async move {
|
||||||
while let Some(state) = client_signal.next().await {
|
while let Result::Ok(event) = client_rx.recv().await {
|
||||||
tx.send_update(state).await;
|
tx.send(event)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -53,50 +58,101 @@ impl Module<GtkBox> for NetworkManagerModule {
|
||||||
|
|
||||||
fn into_widget(
|
fn into_widget(
|
||||||
self,
|
self,
|
||||||
context: WidgetContext<ClientState, ()>,
|
context: WidgetContext<Event, ()>,
|
||||||
info: &ModuleInfo,
|
_info: &ModuleInfo,
|
||||||
) -> Result<ModuleParts<GtkBox>> {
|
) -> Result<ModuleParts<gtk::Box>> {
|
||||||
const INITIAL_ICON_NAME: &str = "content-loading-symbolic";
|
let container = gtk::Box::new(Orientation::Horizontal, 0);
|
||||||
|
|
||||||
let container = GtkBox::new(info.bar_position.orientation(), 0);
|
// Must be done here synchronously to avoid race condition
|
||||||
let icon = Image::new();
|
let rx = context.subscribe();
|
||||||
icon.add_class("icon");
|
// We cannot use recv_glib_async here because the lifetimes don't work out
|
||||||
container.add(&icon);
|
spawn_future_local(handle_update_events(
|
||||||
|
rx,
|
||||||
let image_provider = context.ironbar.image_provider();
|
container.clone(),
|
||||||
|
self.icon_size,
|
||||||
glib::spawn_future_local({
|
context.ironbar.image_provider(),
|
||||||
let image_provider = image_provider.clone();
|
));
|
||||||
let icon = icon.clone();
|
|
||||||
|
|
||||||
async move {
|
|
||||||
image_provider
|
|
||||||
.load_into_image_silent(INITIAL_ICON_NAME, self.icon_size, false, &icon)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
context.subscribe().recv_glib_async((), move |(), state| {
|
|
||||||
let image_provider = image_provider.clone();
|
|
||||||
let icon = icon.clone();
|
|
||||||
|
|
||||||
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",
|
|
||||||
};
|
|
||||||
|
|
||||||
async move {
|
|
||||||
image_provider
|
|
||||||
.load_into_image_silent(icon_name, self.icon_size, false, &icon)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(ModuleParts::new(container, None))
|
Ok(ModuleParts::new(container, None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_update_events(
|
||||||
|
mut rx: broadcast::Receiver<Event>,
|
||||||
|
container: gtk::Box,
|
||||||
|
icon_size: i32,
|
||||||
|
image_provider: Provider,
|
||||||
|
) {
|
||||||
|
let mut icons = HashMap::<String, Image>::new();
|
||||||
|
|
||||||
|
while let Result::Ok(event) = rx.recv().await {
|
||||||
|
match event {
|
||||||
|
Event::DeviceAdded { interface, .. } => {
|
||||||
|
let icon = Image::new();
|
||||||
|
icon.add_class("icon");
|
||||||
|
container.add(&icon);
|
||||||
|
icons.insert(interface, icon);
|
||||||
|
}
|
||||||
|
Event::DeviceStateChanged {
|
||||||
|
interface,
|
||||||
|
r#type,
|
||||||
|
state,
|
||||||
|
} => {
|
||||||
|
let icon = icons
|
||||||
|
.get(&interface)
|
||||||
|
.expect("the icon for the interface to be present");
|
||||||
|
// TODO: Make this configurable at runtime
|
||||||
|
let icon_name = get_icon_for_device_state(&r#type, &state);
|
||||||
|
match icon_name {
|
||||||
|
Some(icon_name) => {
|
||||||
|
image_provider
|
||||||
|
.load_into_image_silent(icon_name, icon_size, false, icon)
|
||||||
|
.await;
|
||||||
|
icon.show();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
icon.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_icon_for_device_state(r#type: &DeviceType, state: &DeviceState) -> Option<&'static str> {
|
||||||
|
match r#type {
|
||||||
|
DeviceType::Ethernet => match state {
|
||||||
|
DeviceState::Unavailable
|
||||||
|
| DeviceState::Disconnected
|
||||||
|
| DeviceState::Prepare
|
||||||
|
| DeviceState::Config
|
||||||
|
| DeviceState::NeedAuth
|
||||||
|
| DeviceState::IpConfig
|
||||||
|
| DeviceState::IpCheck
|
||||||
|
| DeviceState::Secondaries
|
||||||
|
| DeviceState::Deactivating
|
||||||
|
| DeviceState::Failed => Some("icon:network-wired-disconnected-symbolic"),
|
||||||
|
DeviceState::Activated => Some("icon:network-wired-symbolic"),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
DeviceType::Wifi => match state {
|
||||||
|
DeviceState::Unavailable => Some("icon:network-wireless-hardware-disabled-symbolic"),
|
||||||
|
DeviceState::Disconnected
|
||||||
|
| DeviceState::Prepare
|
||||||
|
| DeviceState::Config
|
||||||
|
| DeviceState::NeedAuth
|
||||||
|
| DeviceState::IpConfig
|
||||||
|
| DeviceState::IpCheck
|
||||||
|
| DeviceState::Secondaries
|
||||||
|
| DeviceState::Deactivating
|
||||||
|
| DeviceState::Failed => Some("icon:network-wireless-offline-symbolic"),
|
||||||
|
DeviceState::Activated => Some("icon:network-wireless-connected-symbolic"),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
DeviceType::Tun | DeviceType::Wireguard => match state {
|
||||||
|
DeviceState::Activated => Some("icon:network-vpn-symbolic"),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue