Implement power icon

This commit is contained in:
Jeremy Soller 2024-01-17 12:37:22 -07:00
parent 41cc4d1db7
commit 195525d450
No known key found for this signature in database
GPG key ID: DCFCA852D3906975
6 changed files with 113 additions and 18 deletions

View file

@ -25,6 +25,7 @@ use std::{
ffi::{CStr, CString},
fs,
path::Path,
process,
};
use tokio::{sync::mpsc, task, time};
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
@ -195,6 +196,7 @@ pub enum Message {
Channel(mpsc::Sender<String>),
BackgroundState(cosmic_bg_config::state::State),
NetworkIcon(Option<&'static str>),
PowerInfo(Option<(String, f64)>),
Prompt(String, bool, Option<String>),
Submit,
Suspend,
@ -212,6 +214,7 @@ pub struct App {
surface_names: HashMap<SurfaceId, String>,
text_input_ids: HashMap<SurfaceId, widget::Id>,
network_icon_opt: Option<&'static str>,
power_info_opt: Option<(String, f64)>,
value_tx_opt: Option<mpsc::Sender<String>>,
prompt_opt: Option<(String, bool, Option<String>)>,
error_opt: Option<String>,
@ -305,6 +308,7 @@ impl cosmic::Application for App {
surface_names: HashMap::new(),
text_input_ids: HashMap::new(),
network_icon_opt: None,
power_info_opt: None,
value_tx_opt: None,
prompt_opt: None,
error_opt: None,
@ -388,7 +392,8 @@ impl cosmic::Application for App {
}
}
SessionLockEvent::Unlocked => {
return iced::window::close(SurfaceId::MAIN);
//TODO: cleaner method to exit?
process::exit(0);
}
_ => {}
},
@ -403,6 +408,9 @@ impl cosmic::Application for App {
Message::NetworkIcon(network_icon_opt) => {
self.network_icon_opt = network_icon_opt;
}
Message::PowerInfo(power_info_opt) => {
self.power_info_opt = power_info_opt;
}
Message::Prompt(prompt, secret, value_opt) => {
let prompt_was_none = self.prompt_opt.is_none();
self.prompt_opt = Some((prompt, secret, value_opt));
@ -501,11 +509,12 @@ impl cosmic::Application for App {
status_row = status_row.push(widget::icon::from_name(network_icon));
}
//TODO: get actual status
status_row = status_row.push(iced::widget::row![
widget::icon::from_name("battery-level-50-symbolic"),
widget::text("50%"),
]);
if let Some((power_icon, power_percent)) = &self.power_info_opt {
status_row = status_row.push(iced::widget::row![
widget::icon::from_name(power_icon.clone()),
widget::text(format!("{:.0}%", power_percent)),
]);
}
//TODO: implement these buttons
let button_row = iced::widget::row![
@ -651,16 +660,22 @@ impl cosmic::Application for App {
struct PamSubscription;
//TODO: just use one vec for all subscriptions
let mut network_subscriptions = Vec::with_capacity(1);
let mut extra_suscriptions = Vec::with_capacity(1);
#[cfg(feature = "networkmanager")]
{
network_subscriptions.push(
extra_suscriptions.push(
crate::networkmanager::subscription()
.map(|network_icon_opt| Message::NetworkIcon(network_icon_opt)),
.map(|icon_opt| Message::NetworkIcon(icon_opt)),
);
}
#[cfg(feature = "upower")]
{
extra_suscriptions
.push(crate::upower::subscription().map(|info_opt| Message::PowerInfo(info_opt)));
}
//TODO: how to avoid cloning this on every time subscription is called?
let username = self.flags.current_user.name.clone();
Subscription::batch([
@ -736,7 +751,7 @@ impl cosmic::Application for App {
}
},
),
Subscription::batch(network_subscriptions),
Subscription::batch(extra_suscriptions),
])
}
}

View file

@ -11,6 +11,9 @@ mod logind;
#[cfg(feature = "networkmanager")]
mod networkmanager;
#[cfg(feature = "upower")]
mod upower;
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

View file

@ -1,5 +1,5 @@
use cosmic::iced::{
futures::{channel::mpsc, SinkExt},
futures::{channel::mpsc, SinkExt, StreamExt},
subscription, Subscription,
};
use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager};
@ -65,7 +65,7 @@ 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
let mut active_conns_changed = nm.receive_active_connections_changed().await;
loop {
let mut icon = NetworkIcon::None;
@ -100,7 +100,10 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Option<&'static str>>) -> Result<
msg_tx.send(Some(icon.name())).await.unwrap();
//TODO: select best timeout
time::sleep(time::Duration::new(5, 0)).await;
// Waits until active connections have changed and at least one second has passed
tokio::join!(
active_conns_changed.next(),
time::sleep(time::Duration::from_secs(1))
);
}
}

60
src/upower.rs Normal file
View file

@ -0,0 +1,60 @@
use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
subscription, Subscription,
};
use std::any::TypeId;
use tokio::time;
use upower_dbus::UPowerProxy;
use zbus::{Connection, Result};
pub fn subscription() -> Subscription<Option<(String, f64)>> {
struct PowerSubscription;
subscription::channel(
TypeId::of::<PowerSubscription>(),
16,
|mut msg_tx| async move {
match handler(&mut msg_tx).await {
Ok(()) => {}
Err(err) => {
log::warn!("upower error: {}", err);
//TODO: send error
}
}
// If reading power status failed, clear power 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<(String, f64)>>) -> Result<()> {
let zbus = Connection::system().await?;
let upower = UPowerProxy::new(&zbus).await?;
let dev = upower.get_display_device().await?;
let mut icon_name_changed = dev.receive_icon_name_changed().await;
let mut percentage_changed = dev.receive_percentage_changed().await;
loop {
let mut info_opt = None;
if let Ok(percent) = dev.percentage().await {
if let Ok(icon_name) = dev.icon_name().await {
if !icon_name.is_empty() {
info_opt = Some((icon_name, percent));
}
}
}
msg_tx.send(info_opt).await.unwrap();
// Waits until icon or percentage have changed
tokio::select!(_ = icon_name_changed.next() => (), _ = percentage_changed.next() => ());
}
}