Implement power icon
This commit is contained in:
parent
41cc4d1db7
commit
195525d450
6 changed files with 113 additions and 18 deletions
17
Cargo.lock
generated
17
Cargo.lock
generated
|
|
@ -821,6 +821,7 @@ dependencies = [
|
||||||
"pwd",
|
"pwd",
|
||||||
"shlex",
|
"shlex",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"upower_dbus",
|
||||||
"wayland-client 0.31.1",
|
"wayland-client 0.31.1",
|
||||||
"zbus",
|
"zbus",
|
||||||
]
|
]
|
||||||
|
|
@ -4271,6 +4272,16 @@ version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "upower_dbus"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "git+https://github.com/pop-os/dbus-settings-bindings#3644bc909984842f35adb1057ef6e0a277c1aa6a"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_repr",
|
||||||
|
"zbus",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
|
@ -4726,7 +4737,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.11.2",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
|
|
@ -4751,7 +4762,7 @@ dependencies = [
|
||||||
"codespan-reporting",
|
"codespan-reporting",
|
||||||
"log",
|
"log",
|
||||||
"naga",
|
"naga",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.11.2",
|
||||||
"profiling",
|
"profiling",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
|
|
@ -4791,7 +4802,7 @@ dependencies = [
|
||||||
"naga",
|
"naga",
|
||||||
"objc",
|
"objc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.11.2",
|
||||||
"profiling",
|
"profiling",
|
||||||
"range-alloc",
|
"range-alloc",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ wayland-client = "0.31.1"
|
||||||
cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||||
# For logind integration using logind feature
|
# For logind integration using logind feature
|
||||||
logind-zbus = { version = "3.1.2", optional = true }
|
logind-zbus = { version = "3.1.2", optional = true }
|
||||||
|
# For power status with upower feature
|
||||||
|
upower_dbus = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||||
# Required for some features
|
# Required for some features
|
||||||
zbus = { version = "3.14.1", optional = true }
|
zbus = { version = "3.14.1", optional = true }
|
||||||
|
|
||||||
|
|
@ -39,6 +41,7 @@ version = "1.33.0"
|
||||||
features = ["full"]
|
features = ["full"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["logind", "networkmanager"]
|
default = ["logind", "networkmanager", "upower"]
|
||||||
logind = ["logind-zbus", "zbus"]
|
logind = ["logind-zbus", "zbus"]
|
||||||
networkmanager = ["cosmic-dbus-networkmanager", "zbus"]
|
networkmanager = ["cosmic-dbus-networkmanager", "zbus"]
|
||||||
|
upower = ["upower_dbus", "zbus"]
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use std::{
|
||||||
ffi::{CStr, CString},
|
ffi::{CStr, CString},
|
||||||
fs,
|
fs,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
process,
|
||||||
};
|
};
|
||||||
use tokio::{sync::mpsc, task, time};
|
use tokio::{sync::mpsc, task, time};
|
||||||
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
|
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
|
||||||
|
|
@ -195,6 +196,7 @@ pub enum Message {
|
||||||
Channel(mpsc::Sender<String>),
|
Channel(mpsc::Sender<String>),
|
||||||
BackgroundState(cosmic_bg_config::state::State),
|
BackgroundState(cosmic_bg_config::state::State),
|
||||||
NetworkIcon(Option<&'static str>),
|
NetworkIcon(Option<&'static str>),
|
||||||
|
PowerInfo(Option<(String, f64)>),
|
||||||
Prompt(String, bool, Option<String>),
|
Prompt(String, bool, Option<String>),
|
||||||
Submit,
|
Submit,
|
||||||
Suspend,
|
Suspend,
|
||||||
|
|
@ -212,6 +214,7 @@ pub struct App {
|
||||||
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>,
|
network_icon_opt: Option<&'static str>,
|
||||||
|
power_info_opt: Option<(String, f64)>,
|
||||||
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>,
|
||||||
|
|
@ -305,6 +308,7 @@ impl cosmic::Application for App {
|
||||||
surface_names: HashMap::new(),
|
surface_names: HashMap::new(),
|
||||||
text_input_ids: HashMap::new(),
|
text_input_ids: HashMap::new(),
|
||||||
network_icon_opt: None,
|
network_icon_opt: None,
|
||||||
|
power_info_opt: None,
|
||||||
value_tx_opt: None,
|
value_tx_opt: None,
|
||||||
prompt_opt: None,
|
prompt_opt: None,
|
||||||
error_opt: None,
|
error_opt: None,
|
||||||
|
|
@ -388,7 +392,8 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SessionLockEvent::Unlocked => {
|
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) => {
|
Message::NetworkIcon(network_icon_opt) => {
|
||||||
self.network_icon_opt = 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) => {
|
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));
|
||||||
|
|
@ -501,11 +509,12 @@ impl cosmic::Application for App {
|
||||||
status_row = status_row.push(widget::icon::from_name(network_icon));
|
status_row = status_row.push(widget::icon::from_name(network_icon));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: get actual status
|
if let Some((power_icon, power_percent)) = &self.power_info_opt {
|
||||||
status_row = status_row.push(iced::widget::row![
|
status_row = status_row.push(iced::widget::row![
|
||||||
widget::icon::from_name("battery-level-50-symbolic"),
|
widget::icon::from_name(power_icon.clone()),
|
||||||
widget::text("50%"),
|
widget::text(format!("{:.0}%", power_percent)),
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: implement these buttons
|
//TODO: implement these buttons
|
||||||
let button_row = iced::widget::row![
|
let button_row = iced::widget::row![
|
||||||
|
|
@ -651,16 +660,22 @@ impl cosmic::Application for App {
|
||||||
struct PamSubscription;
|
struct PamSubscription;
|
||||||
|
|
||||||
//TODO: just use one vec for all subscriptions
|
//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")]
|
#[cfg(feature = "networkmanager")]
|
||||||
{
|
{
|
||||||
network_subscriptions.push(
|
extra_suscriptions.push(
|
||||||
crate::networkmanager::subscription()
|
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?
|
//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([
|
||||||
|
|
@ -736,7 +751,7 @@ impl cosmic::Application for App {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Subscription::batch(network_subscriptions),
|
Subscription::batch(extra_suscriptions),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ mod logind;
|
||||||
#[cfg(feature = "networkmanager")]
|
#[cfg(feature = "networkmanager")]
|
||||||
mod networkmanager;
|
mod networkmanager;
|
||||||
|
|
||||||
|
#[cfg(feature = "upower")]
|
||||||
|
mod upower;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use cosmic::iced::{
|
use cosmic::iced::{
|
||||||
futures::{channel::mpsc, SinkExt},
|
futures::{channel::mpsc, SinkExt, StreamExt},
|
||||||
subscription, Subscription,
|
subscription, Subscription,
|
||||||
};
|
};
|
||||||
use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager};
|
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 zbus = Connection::system().await?;
|
||||||
let nm = NetworkManager::new(&zbus).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 {
|
loop {
|
||||||
let mut icon = NetworkIcon::None;
|
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();
|
msg_tx.send(Some(icon.name())).await.unwrap();
|
||||||
|
|
||||||
//TODO: select best timeout
|
// Waits until active connections have changed and at least one second has passed
|
||||||
time::sleep(time::Duration::new(5, 0)).await;
|
tokio::join!(
|
||||||
|
active_conns_changed.next(),
|
||||||
|
time::sleep(time::Duration::from_secs(1))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
src/upower.rs
Normal file
60
src/upower.rs
Normal 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() => ());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue