mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2026-01-08 12:16:44 +01:00
Compare commits
7 commits
develop
...
feat/volum
| Author | SHA1 | Date | |
|---|---|---|---|
|
3483b5e75a |
|||
|
55344bfdae |
|||
|
081678464a |
|||
|
75e61cfd93 |
|||
|
33c69241b7 |
|||
|
4788031f5f |
|||
|
9ea49202b3 |
8 changed files with 299 additions and 347 deletions
76
Cargo.lock
generated
76
Cargo.lock
generated
|
|
@ -736,6 +736,12 @@ dependencies = [
|
|||
"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]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
|
|
@ -1012,6 +1018,22 @@ dependencies = [
|
|||
"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]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
|
|
@ -1123,6 +1145,18 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gensym"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "913dce4c5f06c2ea40fc178c06f777ac89fc6b1383e90c254fafb1abe4ba3c82"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.39",
|
||||
"syn 2.0.99",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
|
@ -1811,6 +1845,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"evdev-rs",
|
||||
"futures-lite",
|
||||
"futures-signals",
|
||||
"glib",
|
||||
"gtk",
|
||||
"gtk-layer-shell",
|
||||
|
|
@ -1834,7 +1869,6 @@ dependencies = [
|
|||
"sysinfo",
|
||||
"system-tray",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"tracing-appender",
|
||||
"tracing-error",
|
||||
|
|
@ -2507,6 +2541,26 @@ dependencies = [
|
|||
"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]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
|
@ -3503,17 +3557,6 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.13"
|
||||
|
|
@ -3822,6 +3865,15 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ music = ["dep:regex"]
|
|||
"music+mpris" = ["music", "mpris"]
|
||||
"music+mpd" = ["music", "mpd-utils"]
|
||||
|
||||
network_manager = ["futures-lite", "tokio-stream", "zbus"]
|
||||
network_manager = ["futures-lite", "futures-signals", "zbus"]
|
||||
|
||||
notifications = ["zbus"]
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ regex = { version = "1.11.1", default-features = false, features = [
|
|||
], optional = true }
|
||||
|
||||
# network_manager
|
||||
tokio-stream = { version = "0.1.17", optional = true }
|
||||
futures-signals = { version = "0.3.34", optional = true }
|
||||
|
||||
# sys_info
|
||||
sysinfo = { version = "0.36.1", optional = true }
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ impl Clients {
|
|||
if let Some(client) = &self.network_manager {
|
||||
Ok(client.clone())
|
||||
} else {
|
||||
let client = networkmanager::create_client()?;
|
||||
let client = await_sync(async move { networkmanager::create_client().await })?;
|
||||
self.network_manager = Some(client.clone());
|
||||
Ok(client)
|
||||
}
|
||||
|
|
|
|||
171
src/clients/networkmanager.rs
Normal file
171
src/clients/networkmanager.rs
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
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);
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
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,
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
use crate::clients::networkmanager::dbus::{DeviceState, DeviceType};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Event {
|
||||
DeviceAdded {
|
||||
interface: String,
|
||||
},
|
||||
DeviceStateChanged {
|
||||
interface: String,
|
||||
r#type: DeviceType,
|
||||
state: DeviceState,
|
||||
},
|
||||
}
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
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,18 +1,16 @@
|
|||
use crate::clients::networkmanager::Client;
|
||||
use crate::clients::networkmanager::dbus::{DeviceState, DeviceType};
|
||||
use crate::clients::networkmanager::event::Event;
|
||||
use crate::channels::{AsyncSenderExt, BroadcastReceiverExt};
|
||||
use crate::clients::networkmanager::{Client, ClientState};
|
||||
use crate::config::CommonConfig;
|
||||
use crate::gtk_helpers::IronbarGtkExt;
|
||||
use crate::image::Provider;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleParts, WidgetContext};
|
||||
use crate::{module_impl, spawn};
|
||||
use color_eyre::{Result, eyre::Ok};
|
||||
use glib::spawn_future_local;
|
||||
use gtk::prelude::{ContainerExt, WidgetExt};
|
||||
use gtk::{Image, Orientation};
|
||||
use color_eyre::Result;
|
||||
use futures_lite::StreamExt;
|
||||
use futures_signals::signal::SignalExt;
|
||||
use gtk::prelude::ContainerExt;
|
||||
use gtk::{Box as GtkBox, Image};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
use tokio::sync::mpsc::Receiver;
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
|
||||
|
|
@ -28,29 +26,26 @@ const fn default_icon_size() -> i32 {
|
|||
24
|
||||
}
|
||||
|
||||
impl Module<gtk::Box> for NetworkManagerModule {
|
||||
type SendMessage = Event;
|
||||
impl Module<GtkBox> for NetworkManagerModule {
|
||||
type SendMessage = ClientState;
|
||||
type ReceiveMessage = ();
|
||||
|
||||
module_impl!("network_manager");
|
||||
|
||||
fn spawn_controller(
|
||||
&self,
|
||||
_info: &ModuleInfo,
|
||||
context: &WidgetContext<Event, ()>,
|
||||
_rx: mpsc::Receiver<()>,
|
||||
_: &ModuleInfo,
|
||||
context: &WidgetContext<ClientState, ()>,
|
||||
_: Receiver<()>,
|
||||
) -> Result<()> {
|
||||
let client = context.try_client::<Client>()?;
|
||||
// Should we be using context.tx with ModuleUpdateEvent::Update instead?
|
||||
let tx = context.update_tx.clone();
|
||||
// Must be done here synchronously to avoid race condition
|
||||
let mut client_rx = client.subscribe();
|
||||
spawn(async move {
|
||||
while let Result::Ok(event) = client_rx.recv().await {
|
||||
tx.send(event)?;
|
||||
}
|
||||
let mut client_signal = client.subscribe().to_stream();
|
||||
let tx = context.tx.clone();
|
||||
|
||||
Ok(())
|
||||
spawn(async move {
|
||||
while let Some(state) = client_signal.next().await {
|
||||
tx.send_update(state).await;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
|
@ -58,101 +53,50 @@ impl Module<gtk::Box> for NetworkManagerModule {
|
|||
|
||||
fn into_widget(
|
||||
self,
|
||||
context: WidgetContext<Event, ()>,
|
||||
_info: &ModuleInfo,
|
||||
) -> Result<ModuleParts<gtk::Box>> {
|
||||
let container = gtk::Box::new(Orientation::Horizontal, 0);
|
||||
context: WidgetContext<ClientState, ()>,
|
||||
info: &ModuleInfo,
|
||||
) -> Result<ModuleParts<GtkBox>> {
|
||||
const INITIAL_ICON_NAME: &str = "content-loading-symbolic";
|
||||
|
||||
// Must be done here synchronously to avoid race condition
|
||||
let rx = context.subscribe();
|
||||
// We cannot use recv_glib_async here because the lifetimes don't work out
|
||||
spawn_future_local(handle_update_events(
|
||||
rx,
|
||||
container.clone(),
|
||||
self.icon_size,
|
||||
context.ironbar.image_provider(),
|
||||
));
|
||||
let container = GtkBox::new(info.bar_position.orientation(), 0);
|
||||
let icon = Image::new();
|
||||
icon.add_class("icon");
|
||||
container.add(&icon);
|
||||
|
||||
let image_provider = context.ironbar.image_provider();
|
||||
|
||||
glib::spawn_future_local({
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
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