Implement network icon
This commit is contained in:
parent
d4d2773715
commit
41cc4d1db7
3 changed files with 139 additions and 11 deletions
|
|
@ -194,6 +194,7 @@ pub enum Message {
|
||||||
SessionLockEvent(SessionLockEvent),
|
SessionLockEvent(SessionLockEvent),
|
||||||
Channel(mpsc::Sender<String>),
|
Channel(mpsc::Sender<String>),
|
||||||
BackgroundState(cosmic_bg_config::state::State),
|
BackgroundState(cosmic_bg_config::state::State),
|
||||||
|
NetworkIcon(Option<&'static str>),
|
||||||
Prompt(String, bool, Option<String>),
|
Prompt(String, bool, Option<String>),
|
||||||
Submit,
|
Submit,
|
||||||
Suspend,
|
Suspend,
|
||||||
|
|
@ -210,6 +211,7 @@ pub struct App {
|
||||||
surface_images: HashMap<SurfaceId, widget::image::Handle>,
|
surface_images: HashMap<SurfaceId, widget::image::Handle>,
|
||||||
surface_names: HashMap<SurfaceId, String>,
|
surface_names: HashMap<SurfaceId, String>,
|
||||||
text_input_ids: HashMap<SurfaceId, widget::Id>,
|
text_input_ids: HashMap<SurfaceId, widget::Id>,
|
||||||
|
network_icon_opt: Option<&'static str>,
|
||||||
value_tx_opt: Option<mpsc::Sender<String>>,
|
value_tx_opt: Option<mpsc::Sender<String>>,
|
||||||
prompt_opt: Option<(String, bool, Option<String>)>,
|
prompt_opt: Option<(String, bool, Option<String>)>,
|
||||||
error_opt: Option<String>,
|
error_opt: Option<String>,
|
||||||
|
|
@ -302,6 +304,7 @@ impl cosmic::Application for App {
|
||||||
surface_images: HashMap::new(),
|
surface_images: HashMap::new(),
|
||||||
surface_names: HashMap::new(),
|
surface_names: HashMap::new(),
|
||||||
text_input_ids: HashMap::new(),
|
text_input_ids: HashMap::new(),
|
||||||
|
network_icon_opt: None,
|
||||||
value_tx_opt: None,
|
value_tx_opt: None,
|
||||||
prompt_opt: None,
|
prompt_opt: None,
|
||||||
error_opt: None,
|
error_opt: None,
|
||||||
|
|
@ -397,6 +400,9 @@ impl cosmic::Application for App {
|
||||||
self.surface_images.clear();
|
self.surface_images.clear();
|
||||||
self.update_wallpapers();
|
self.update_wallpapers();
|
||||||
}
|
}
|
||||||
|
Message::NetworkIcon(network_icon_opt) => {
|
||||||
|
self.network_icon_opt = network_icon_opt;
|
||||||
|
}
|
||||||
Message::Prompt(prompt, secret, value_opt) => {
|
Message::Prompt(prompt, secret, value_opt) => {
|
||||||
let prompt_was_none = self.prompt_opt.is_none();
|
let prompt_was_none = self.prompt_opt.is_none();
|
||||||
self.prompt_opt = Some((prompt, secret, value_opt));
|
self.prompt_opt = Some((prompt, secret, value_opt));
|
||||||
|
|
@ -489,16 +495,17 @@ impl cosmic::Application for App {
|
||||||
column
|
column
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut status_row = widget::row::with_capacity(2).padding(16.0).spacing(12.0);
|
||||||
|
|
||||||
|
if let Some(network_icon) = self.network_icon_opt {
|
||||||
|
status_row = status_row.push(widget::icon::from_name(network_icon));
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: get actual status
|
//TODO: get actual status
|
||||||
let status_row = iced::widget::row![
|
status_row = status_row.push(iced::widget::row![
|
||||||
widget::icon::from_name("network-wireless-signal-ok-symbolic",),
|
widget::icon::from_name("battery-level-50-symbolic"),
|
||||||
iced::widget::row![
|
widget::text("50%"),
|
||||||
widget::icon::from_name("battery-level-50-symbolic"),
|
]);
|
||||||
widget::text("50%"),
|
|
||||||
]
|
|
||||||
]
|
|
||||||
.padding(16.0)
|
|
||||||
.spacing(12.0);
|
|
||||||
|
|
||||||
//TODO: implement these buttons
|
//TODO: implement these buttons
|
||||||
let button_row = iced::widget::row![
|
let button_row = iced::widget::row![
|
||||||
|
|
@ -643,6 +650,17 @@ impl cosmic::Application for App {
|
||||||
struct HeartbeatSubscription;
|
struct HeartbeatSubscription;
|
||||||
struct PamSubscription;
|
struct PamSubscription;
|
||||||
|
|
||||||
|
//TODO: just use one vec for all subscriptions
|
||||||
|
let mut network_subscriptions = Vec::with_capacity(1);
|
||||||
|
|
||||||
|
#[cfg(feature = "networkmanager")]
|
||||||
|
{
|
||||||
|
network_subscriptions.push(
|
||||||
|
crate::networkmanager::subscription()
|
||||||
|
.map(|network_icon_opt| Message::NetworkIcon(network_icon_opt)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: how to avoid cloning this on every time subscription is called?
|
//TODO: how to avoid cloning this on every time subscription is called?
|
||||||
let username = self.flags.current_user.name.clone();
|
let username = self.flags.current_user.name.clone();
|
||||||
Subscription::batch([
|
Subscription::batch([
|
||||||
|
|
@ -707,17 +725,18 @@ impl cosmic::Application for App {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::info!("authentication error: {:?}", err);
|
log::warn!("authentication error: {}", err);
|
||||||
msg_tx.send(Message::Error(err.to_string())).await.unwrap();
|
msg_tx.send(Message::Error(err.to_string())).await.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
time::sleep(time::Duration::new(1, 0)).await;
|
time::sleep(time::Duration::new(60, 0)).await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Subscription::batch(network_subscriptions),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ mod locker;
|
||||||
#[cfg(feature = "logind")]
|
#[cfg(feature = "logind")]
|
||||||
mod logind;
|
mod logind;
|
||||||
|
|
||||||
|
#[cfg(feature = "networkmanager")]
|
||||||
|
mod networkmanager;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
|
|
||||||
106
src/networkmanager.rs
Normal file
106
src/networkmanager.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
use cosmic::iced::{
|
||||||
|
futures::{channel::mpsc, SinkExt},
|
||||||
|
subscription, Subscription,
|
||||||
|
};
|
||||||
|
use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager};
|
||||||
|
use std::{any::TypeId, cmp};
|
||||||
|
use tokio::time;
|
||||||
|
use zbus::{Connection, Result};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum NetworkIcon {
|
||||||
|
None,
|
||||||
|
Wired,
|
||||||
|
Wireless(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetworkIcon {
|
||||||
|
pub fn name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
NetworkIcon::None => "network-wired-disconnected-symbolic",
|
||||||
|
NetworkIcon::Wired => "network-wired-symbolic",
|
||||||
|
NetworkIcon::Wireless(strength) => {
|
||||||
|
if *strength < 25 {
|
||||||
|
"network-wireless-signal-weak-symbolic"
|
||||||
|
} else if *strength < 50 {
|
||||||
|
"network-wireless-signal-ok-symbolic"
|
||||||
|
} else if *strength < 75 {
|
||||||
|
"network-wireless-signal-good-symbolic"
|
||||||
|
} else {
|
||||||
|
"network-wireless-signal-excellent-symbolic"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscription() -> Subscription<Option<&'static str>> {
|
||||||
|
struct NetworkSubscription;
|
||||||
|
|
||||||
|
subscription::channel(
|
||||||
|
TypeId::of::<NetworkSubscription>(),
|
||||||
|
16,
|
||||||
|
|mut msg_tx| async move {
|
||||||
|
match handler(&mut msg_tx).await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("networkmanager error: {}", err);
|
||||||
|
//TODO: send error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If reading network status failed, clear network icon
|
||||||
|
msg_tx.send(None).await.unwrap();
|
||||||
|
|
||||||
|
//TODO: should we retry on error?
|
||||||
|
loop {
|
||||||
|
time::sleep(time::Duration::new(60, 0)).await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: use never type?
|
||||||
|
pub async fn handler(msg_tx: &mut mpsc::Sender<Option<&'static str>>) -> Result<()> {
|
||||||
|
let zbus = Connection::system().await?;
|
||||||
|
let nm = NetworkManager::new(&zbus).await?;
|
||||||
|
|
||||||
|
//TOOD: use receive_active_connections_changed
|
||||||
|
loop {
|
||||||
|
let mut icon = NetworkIcon::None;
|
||||||
|
|
||||||
|
for conn in nm.active_connections().await.unwrap_or_default() {
|
||||||
|
for dev in conn.devices().await.unwrap_or_default() {
|
||||||
|
match dev.downcast_to_device().await.unwrap_or_default() {
|
||||||
|
//TODO: more specific devices
|
||||||
|
Some(SpecificDevice::Wired(_)) => {
|
||||||
|
// Wired only overrides None
|
||||||
|
icon = match icon {
|
||||||
|
NetworkIcon::None => NetworkIcon::Wired,
|
||||||
|
other => other,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(SpecificDevice::Wireless(wireless)) => {
|
||||||
|
if let Ok(ap) = wireless.active_access_point().await {
|
||||||
|
if let Ok(strength) = ap.strength().await {
|
||||||
|
// Wireless always overrides with the highest strength
|
||||||
|
icon = match icon {
|
||||||
|
NetworkIcon::Wireless(other_strength) => {
|
||||||
|
NetworkIcon::Wireless(cmp::max(strength, other_strength))
|
||||||
|
}
|
||||||
|
_ => NetworkIcon::Wireless(strength),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_tx.send(Some(icon.name())).await.unwrap();
|
||||||
|
|
||||||
|
//TODO: select best timeout
|
||||||
|
time::sleep(time::Duration::new(5, 0)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue