refactor: improve and fix subscriptions and async logic

This commit is contained in:
Michael Aaron Murphy 2025-04-10 13:03:53 +02:00 committed by Ashley Wulber
parent 2d2543094e
commit 0600931abf
9 changed files with 290 additions and 269 deletions

107
Cargo.lock generated
View file

@ -141,12 +141,6 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "allocator-api2"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]] [[package]]
name = "almost" name = "almost"
version = "0.2.0" version = "0.2.0"
@ -728,39 +722,6 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cached"
version = "0.55.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0839c297f8783316fcca9d90344424e968395413f0662a5481f79c6648bbc14"
dependencies = [
"ahash",
"cached_proc_macro",
"cached_proc_macro_types",
"hashbrown 0.14.5",
"once_cell",
"thiserror 2.0.12",
"web-time",
]
[[package]]
name = "cached_proc_macro"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673992d934f0711b68ebb3e1b79cdc4be31634b37c98f26867ced0438ca5c603"
dependencies = [
"darling 0.20.10",
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
[[package]] [[package]]
name = "calloop" name = "calloop"
version = "0.13.0" version = "0.13.0"
@ -1061,7 +1022,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-bg-config" name = "cosmic-bg-config"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/cosmic-bg#b6adf25075383c0e606658a7309919a9b092ee54" source = "git+https://github.com/pop-os/cosmic-bg#d41c8506ed5c44afd51f74bdb56f620e1dec1ffc"
dependencies = [ dependencies = [
"colorgrad", "colorgrad",
"cosmic-config", "cosmic-config",
@ -1087,7 +1048,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-comp-config" name = "cosmic-comp-config"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/cosmic-comp#29a649541d527021e98090f2ccd486cf21335dab" source = "git+https://github.com/pop-os/cosmic-comp#99bbd10168aed50a24db730cf20eb778e072c5e4"
dependencies = [ dependencies = [
"cosmic-config", "cosmic-config",
"input", "input",
@ -1097,7 +1058,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config" name = "cosmic-config"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#0aa518984ec6bb29c368b879c20b971f04fbc0c7" source = "git+https://github.com/pop-os/libcosmic#0ddde755ee922edcff1a3b11cc00753cb29fe3b0"
dependencies = [ dependencies = [
"atomicwrites", "atomicwrites",
"calloop 0.14.2", "calloop 0.14.2",
@ -1120,7 +1081,7 @@ dependencies = [
[[package]] [[package]]
name = "cosmic-config-derive" name = "cosmic-config-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/pop-os/libcosmic#0aa518984ec6bb29c368b879c20b971f04fbc0c7" source = "git+https://github.com/pop-os/libcosmic#0ddde755ee922edcff1a3b11cc00753cb29fe3b0"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",
@ -1165,7 +1126,7 @@ dependencies = [
"cosmic-dbus-networkmanager", "cosmic-dbus-networkmanager",
"cosmic-greeter-config", "cosmic-greeter-config",
"cosmic-greeter-daemon", "cosmic-greeter-daemon",
"dirs 5.0.1", "dirs 6.0.0",
"env_logger", "env_logger",
"freedesktop_entry_parser", "freedesktop_entry_parser",
"futures-util", "futures-util",
@ -1178,7 +1139,7 @@ dependencies = [
"nix 0.29.0", "nix 0.29.0",
"pam-client", "pam-client",
"pwd", "pwd",
"ron 0.8.1", "ron 0.10.1",
"rust-embed", "rust-embed",
"shlex", "shlex",
"tokio", "tokio",
@ -1212,7 +1173,7 @@ dependencies = [
"log", "log",
"nix 0.29.0", "nix 0.29.0",
"pwd", "pwd",
"ron 0.8.1", "ron 0.10.1",
"serde", "serde",
"tokio", "tokio",
"zbus 4.4.0", "zbus 4.4.0",
@ -1738,7 +1699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -2017,16 +1978,15 @@ dependencies = [
[[package]] [[package]]
name = "freedesktop-desktop-entry" name = "freedesktop-desktop-entry"
version = "0.7.9" version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e5e2bd2e383df08a8439c2e096be16d5355aa00f976b295cf8e077ea5953d5d" checksum = "2258b98a780699da05c858682498ceaf3942013d7d93ca7584e26fbacc58f2d9"
dependencies = [ dependencies = [
"cached",
"gettext-rs", "gettext-rs",
"log", "log",
"memchr", "memchr",
"strsim 0.11.1",
"thiserror 2.0.12", "thiserror 2.0.12",
"unicase",
"xdg", "xdg",
] ]
@ -2386,10 +2346,6 @@ name = "hashbrown"
version = "0.14.5" version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
@ -2580,7 +2536,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.14.0-dev" version = "0.14.0-dev"
source = "git+https://github.com/pop-os/libcosmic#0aa518984ec6bb29c368b879c20b971f04fbc0c7" source = "git+https://github.com/pop-os/libcosmic#0ddde755ee922edcff1a3b11cc00753cb29fe3b0"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.0",
"bytes", "bytes",
@ -2604,7 +2560,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.14.0-dev" version = "0.14.0-dev"
source = "git+https://github.com/pop-os/libcosmic#0aa518984ec6bb29c368b879c20b971f04fbc0c7" source = "git+https://github.com/pop-os/libcosmic#0ddde755ee922edcff1a3b11cc00753cb29fe3b0"
dependencies = [ dependencies = [
"futures", "futures",
"iced_core", "iced_core",
@ -3049,7 +3005,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [ dependencies = [
"hermit-abi 0.5.0", "hermit-abi 0.5.0",
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -3258,7 +3214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@ -3696,7 +3652,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [ dependencies = [
"proc-macro-crate 3.3.0", "proc-macro-crate 1.3.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.100", "syn 2.0.100",
@ -4648,6 +4604,19 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "ron"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f"
dependencies = [
"base64 0.22.1",
"bitflags 2.9.0",
"serde",
"serde_derive",
"unicode-ident",
]
[[package]] [[package]]
name = "roxmltree" name = "roxmltree"
version = "0.20.0" version = "0.20.0"
@ -4742,7 +4711,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -4755,7 +4724,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.9.3", "linux-raw-sys 0.9.3",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -5220,7 +5189,7 @@ dependencies = [
"getrandom 0.3.2", "getrandom 0.3.2",
"once_cell", "once_cell",
"rustix 1.0.3", "rustix 1.0.3",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@ -5371,9 +5340,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.44.1" version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bytes", "bytes",
@ -5554,6 +5523,12 @@ dependencies = [
"tinystr", "tinystr",
] ]
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.18" version = "0.3.18"
@ -6106,7 +6081,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]

View file

@ -13,7 +13,7 @@ cosmic-comp-config.workspace = true
cosmic-config = { workspace = true, features = ["calloop", "macro"] } cosmic-config = { workspace = true, features = ["calloop", "macro"] }
cosmic-greeter-config.workspace = true cosmic-greeter-config.workspace = true
cosmic-greeter-daemon = { path = "daemon" } cosmic-greeter-daemon = { path = "daemon" }
dirs = "5" dirs = "6"
env_logger.workspace = true env_logger.workspace = true
freedesktop_entry_parser = "1.3.0" freedesktop_entry_parser = "1.3.0"
libcosmic = { workspace = true, features = [ libcosmic = { workspace = true, features = [
@ -34,7 +34,7 @@ xkb-data = "0.2"
xdg = "2.5.2" xdg = "2.5.2"
#TODO: reduce features #TODO: reduce features
tokio = { workspace = true, features = ["full"] } tokio = { workspace = true, features = ["full"] }
wayland-client = "0.31.5" wayland-client = "0.31.8"
# For network status using networkmanager feature # For network status using networkmanager feature
cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", rev = "badfc6a", optional = true } cosmic-dbus-networkmanager = { git = "https://github.com/pop-os/dbus-settings-bindings", rev = "badfc6a", optional = true }
# For logind integration using logind feature # For logind integration using logind feature
@ -81,7 +81,7 @@ members = ["cosmic-greeter-config", "daemon"]
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
rust-version = "1.79.0" rust-version = "1.85.0"
[workspace.dependencies] [workspace.dependencies]
env_logger = "0.10.2" env_logger = "0.10.2"
@ -89,7 +89,7 @@ log = "0.4.22"
# Fix zbus compilation by manually adding nix with user feature # Fix zbus compilation by manually adding nix with user feature
nix = { version = "0.29", features = ["user"] } nix = { version = "0.29", features = ["user"] }
pwd = "1.4.0" pwd = "1.4.0"
ron = "0.8" ron = "0.10.1"
serde = "1" serde = "1"
tokio = "1.39.1" tokio = "1.39.1"
zbus = "4" zbus = "4"

View file

@ -33,7 +33,7 @@ where
Ok(handler) => { Ok(handler) => {
let config = C::get_entry(&handler) let config = C::get_entry(&handler)
.inspect_err(|(errors, _)| { .inspect_err(|(errors, _)| {
for err in errors { for err in errors.iter().filter(|err| err.is_err()) {
log::error!("{err}") log::error!("{err}")
} }
}) })

View file

@ -6,37 +6,37 @@ mod ipc;
use cosmic::app::{Core, Settings, Task}; use cosmic::app::{Core, Settings, Task};
use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity; use cosmic::cctk::wayland_protocols::xdg::shell::client::xdg_positioner::Gravity;
use cosmic::iced::{Point, Size}; use cosmic::iced::{Point, Size};
use cosmic::iced_core::{image, window}; use cosmic::iced_core::image;
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings; use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
use cosmic::surface; use cosmic::surface;
use cosmic::widget::text; use cosmic::widget::text;
use cosmic::{ use cosmic::{
Element,
cosmic_config::{self, ConfigSet, CosmicConfigEntry}, cosmic_config::{self, ConfigSet, CosmicConfigEntry},
executor, executor,
iced::{ iced::{
self, alignment, self, Background, Border, Length, Subscription, alignment,
event::{ event::{
self, self,
wayland::{Event as WaylandEvent, LayerEvent, OutputEvent}, wayland::{Event as WaylandEvent, OutputEvent},
}, },
futures::SinkExt, futures::SinkExt,
platform_specific::{ platform_specific::{
runtime::wayland::layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings}, runtime::wayland::layer_surface::{IcedMargin, IcedOutput, SctkLayerSurfaceSettings},
shell::wayland::commands::layer_surface::{ shell::wayland::commands::layer_surface::{
destroy_layer_surface, get_layer_surface, Anchor, KeyboardInteractivity, Layer, Anchor, KeyboardInteractivity, Layer, destroy_layer_surface, get_layer_surface,
}, },
}, },
Background, Border, Length, Subscription,
}, },
iced_runtime::core::window::Id as SurfaceId, iced_runtime::core::window::Id as SurfaceId,
style, theme, widget, Element, style, theme, widget,
}; };
use cosmic_comp_config::CosmicCompConfig; use cosmic_comp_config::CosmicCompConfig;
use cosmic_greeter_config::Config as CosmicGreeterConfig; use cosmic_greeter_config::Config as CosmicGreeterConfig;
use cosmic_greeter_daemon::{UserData, WallpaperData}; use cosmic_greeter_daemon::{UserData, WallpaperData};
use greetd_ipc::Request; use greetd_ipc::Request;
use std::{ use std::{
collections::{hash_map, HashMap}, collections::{HashMap, hash_map},
error::Error, error::Error,
fs, io, fs, io,
num::NonZeroU32, num::NonZeroU32,
@ -46,8 +46,8 @@ use std::{
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use tokio::time; use tokio::time;
use wayland_client::{protocol::wl_output::WlOutput, Proxy}; use wayland_client::{Proxy, protocol::wl_output::WlOutput};
use zbus::{proxy, Connection}; use zbus::{Connection, proxy};
use crate::fl; use crate::fl;
@ -407,7 +407,6 @@ pub enum Message {
KeyboardLayout(usize), KeyboardLayout(usize),
Login, Login,
NetworkIcon(Option<&'static str>), NetworkIcon(Option<&'static str>),
None,
OutputEvent(OutputEvent, WlOutput), OutputEvent(OutputEvent, WlOutput),
PowerInfo(Option<(String, f64)>), PowerInfo(Option<(String, f64)>),
Prompt(String, bool, Option<String>), Prompt(String, bool, Option<String>),
@ -444,10 +443,11 @@ pub struct App {
dialog_page_opt: Option<DialogPage>, dialog_page_opt: Option<DialogPage>,
dropdown_opt: Option<Dropdown>, dropdown_opt: Option<Dropdown>,
window_size: HashMap<SurfaceId, Size>, window_size: HashMap<SurfaceId, Size>,
heartbeat_handle: Option<cosmic::iced::task::Handle>,
} }
impl App { impl App {
fn menu<'a>(&'a self, id: SurfaceId) -> Element<'a, Message> { fn menu(&self, id: SurfaceId) -> Element<Message> {
let left_element = { let left_element = {
let date_time_column = { let date_time_column = {
let mut column = widget::column::with_capacity(2).padding(16.0).spacing(12.0); let mut column = widget::column::with_capacity(2).padding(16.0).spacing(12.0);
@ -666,7 +666,7 @@ impl App {
} }
SocketState::Open => { SocketState::Open => {
for user_data in &self.flags.user_datas { for user_data in &self.flags.user_datas {
if &user_data.name == &self.selected_username.username { if user_data.name == self.selected_username.username {
match &user_data.icon_opt { match &user_data.icon_opt {
Some(icon) => { Some(icon) => {
column = column.push( column = column.push(
@ -832,16 +832,13 @@ impl App {
} }
} }
/// Send a [`Request`] to the greetd IPC subscription. /// Send a [`Request`] to the greetd IPC subscription.
fn send_request(&self, request: Request) -> Task<Message> { fn send_request(&self, request: Request) {
if let Some(ref sender) = self.greetd_sender { if let Some(ref sender) = self.greetd_sender {
let sender = sender.clone(); let sender = sender.clone();
return cosmic::task::future(async move { tokio::task::spawn(async move {
_ = sender.send(request).await; _ = sender.send(request).await;
cosmic::action::none()
}); });
} }
Task::none()
} }
fn set_xkb_config(&self) { fn set_xkb_config(&self) {
@ -1073,6 +1070,7 @@ impl cosmic::Application for App {
dialog_page_opt: None, dialog_page_opt: None,
dropdown_opt: None, dropdown_opt: None,
window_size: HashMap::new(), window_size: HashMap::new(),
heartbeat_handle: None,
}; };
(app, Task::none()) (app, Task::none())
} }
@ -1080,7 +1078,6 @@ impl cosmic::Application for App {
/// Handle application events here. /// Handle application events here.
fn update(&mut self, message: Self::Message) -> Task<Message> { fn update(&mut self, message: Self::Message) -> Task<Message> {
match message { match message {
Message::None => {}
Message::OutputEvent(output_event, output) => { Message::OutputEvent(output_event, output) => {
match output_event { match output_event {
OutputEvent::Created(output_info_opt) => { OutputEvent::Created(output_info_opt) => {
@ -1213,7 +1210,7 @@ impl cosmic::Application for App {
match &self.socket_state { match &self.socket_state {
SocketState::Open => { SocketState::Open => {
// When socket is opened, send create session // When socket is opened, send create session
return self.send_request(Request::CreateSession { self.send_request(Request::CreateSession {
username: self.selected_username.username.clone(), username: self.selected_username.username.clone(),
}); });
} }
@ -1278,7 +1275,7 @@ impl cosmic::Application for App {
match &self.socket_state { match &self.socket_state {
SocketState::Open => { SocketState::Open => {
self.prompt_opt = None; self.prompt_opt = None;
return self.send_request(Request::CancelSession); self.send_request(Request::CancelSession);
} }
_ => {} _ => {}
} }
@ -1351,55 +1348,56 @@ impl cosmic::Application for App {
Message::Auth(response) => { Message::Auth(response) => {
self.prompt_opt = None; self.prompt_opt = None;
self.error_opt = None; self.error_opt = None;
return self.send_request(Request::PostAuthMessageResponse { response }); self.send_request(Request::PostAuthMessageResponse { response });
} }
Message::Login => { Message::Login => {
self.prompt_opt = None; self.prompt_opt = None;
self.error_opt = None; self.error_opt = None;
match self.flags.sessions.get(&self.selected_session).cloned() { match self.flags.sessions.get(&self.selected_session).cloned() {
Some((cmd, env)) => { Some((cmd, env)) => {
return Task::batch([ self.send_request(Request::StartSession { cmd, env });
self.update(Message::ConfigUpdateUser), return self.update(Message::ConfigUpdateUser);
self.send_request(Request::StartSession { cmd, env }),
]);
} }
None => todo!("session {:?} not found", self.selected_session), None => todo!("session {:?} not found", self.selected_session),
} }
} }
Message::Error(error) => { Message::Error(error) => {
self.error_opt = Some(error); self.error_opt = Some(error);
return self.send_request(Request::CancelSession); self.send_request(Request::CancelSession);
} }
Message::Reconnect => { Message::Reconnect => {
return self.update_user_config(); return self.update_user_config();
} }
Message::DialogCancel => { Message::DialogCancel => {
self.dialog_page_opt = None; self.dialog_page_opt = None;
if let Some(handle) = self.heartbeat_handle.take() {
handle.abort();
}
} }
Message::DialogConfirm => match self.dialog_page_opt.take() { Message::DialogConfirm => match self.dialog_page_opt.take() {
Some(DialogPage::Restart(_)) => { Some(DialogPage::Restart(_)) => {
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
return cosmic::task::future(async move { return cosmic::task::future::<(), ()>(async move {
match crate::logind::reboot().await { match crate::logind::reboot().await {
Ok(()) => (), Ok(()) => (),
Err(err) => { Err(err) => {
log::error!("failed to reboot: {:?}", err); log::error!("failed to reboot: {:?}", err);
} }
} }
cosmic::action::none() })
}); .discard();
} }
Some(DialogPage::Shutdown(_)) => { Some(DialogPage::Shutdown(_)) => {
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
return cosmic::task::future(async move { return cosmic::task::future::<(), ()>(async move {
match crate::logind::power_off().await { match crate::logind::power_off().await {
Ok(()) => (), Ok(()) => (),
Err(err) => { Err(err) => {
log::error!("failed to power off: {:?}", err); log::error!("failed to power off: {:?}", err);
} }
} }
cosmic::action::none() })
}); .discard();
} }
None => {} None => {}
}, },
@ -1421,21 +1419,46 @@ impl cosmic::Application for App {
} }
Message::Suspend => { Message::Suspend => {
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
return cosmic::task::future(async move { return cosmic::task::future::<(), ()>(async move {
match crate::logind::suspend().await { match crate::logind::suspend().await {
Ok(()) => (), Ok(()) => (),
Err(err) => { Err(err) => {
log::error!("failed to suspend: {:?}", err); log::error!("failed to suspend: {:?}", err);
} }
} }
cosmic::action::none() })
.discard();
}
Message::Restart | Message::Shutdown => {
let instant = Instant::now();
self.dialog_page_opt = Some(if matches!(message, Message::Restart) {
DialogPage::Restart(instant)
} else {
DialogPage::Shutdown(instant)
}); });
}
Message::Restart => { if self.heartbeat_handle.is_none() {
self.dialog_page_opt = Some(DialogPage::Restart(Instant::now())); let (heartbeat, handle) = cosmic::task::stream(
} cosmic::iced_futures::stream::channel(1, |mut msg_tx| async move {
Message::Shutdown => { let mut interval = time::interval(Duration::from_secs(1));
self.dialog_page_opt = Some(DialogPage::Shutdown(Instant::now()));
loop {
// Send heartbeat once a second to update time
msg_tx
.send(cosmic::Action::App(Message::Heartbeat))
.await
.unwrap();
interval.tick().await;
}
}),
)
.abortable();
self.heartbeat_handle = Some(handle);
return heartbeat;
}
} }
Message::Heartbeat => match self.dialog_page_opt { Message::Heartbeat => match self.dialog_page_opt {
Some(DialogPage::Restart(instant)) | Some(DialogPage::Shutdown(instant)) => { Some(DialogPage::Restart(instant)) | Some(DialogPage::Shutdown(instant)) => {
@ -1500,8 +1523,6 @@ impl cosmic::Application for App {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
struct HeartbeatSubscription;
let mut subscriptions = vec![ let mut subscriptions = vec![
event::listen_with(|event, _, id| match event { event::listen_with(|event, _, id| match event {
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland( iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(
@ -1516,32 +1537,17 @@ impl cosmic::Application for App {
iced::Event::Window(iced::window::Event::Focused) => Some(Message::Focus(id)), iced::Event::Window(iced::window::Event::Focused) => Some(Message::Focus(id)),
_ => None, _ => None,
}), }),
Subscription::run_with_id(
std::any::TypeId::of::<HeartbeatSubscription>(),
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
loop {
// Send heartbeat once a second to update time
//TODO: only send this when needed
msg_tx.send(Message::Heartbeat).await.unwrap();
time::sleep(time::Duration::new(1, 0)).await;
}
}),
),
ipc::subscription(), ipc::subscription(),
]; ];
#[cfg(feature = "networkmanager")] #[cfg(feature = "networkmanager")]
{ {
subscriptions.push( subscriptions.push(crate::networkmanager::subscription().map(Message::NetworkIcon));
crate::networkmanager::subscription()
.map(|icon_opt| Message::NetworkIcon(icon_opt)),
);
} }
#[cfg(feature = "upower")] #[cfg(feature = "upower")]
{ {
subscriptions subscriptions.push(crate::upower::subscription().map(Message::PowerInfo));
.push(crate::upower::subscription().map(|info_opt| Message::PowerInfo(info_opt)));
} }
Subscription::batch(subscriptions) Subscription::batch(subscriptions)

View file

@ -6,6 +6,7 @@ use cosmic::iced::Subscription;
use futures_util::SinkExt; use futures_util::SinkExt;
use greetd_ipc::codec::TokioCodec; use greetd_ipc::codec::TokioCodec;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use tokio::net::UnixStream; use tokio::net::UnixStream;
use tokio::sync::mpsc; use tokio::sync::mpsc;
@ -20,12 +21,15 @@ pub fn subscription() -> Subscription<Message> {
let socket_path = let socket_path =
std::env::var_os("GREETD_SOCK").expect("GREETD_SOCK environment not set"); std::env::var_os("GREETD_SOCK").expect("GREETD_SOCK environment not set");
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop { loop {
_ = sender.send(Message::Reconnect).await; _ = sender.send(Message::Reconnect).await;
let mut stream = match UnixStream::connect(&socket_path).await { let mut stream = match UnixStream::connect(&socket_path).await {
Ok(stream) => stream, Ok(stream) => stream,
Err(why) => { Err(why) => {
log::error!("greetd IPC socket connection failed: {why:?}");
_ = sender.send(Message::Socket(SocketState::Error(Arc::new(why)))); _ = sender.send(Message::Socket(SocketState::Error(Arc::new(why))));
break; break;
@ -36,7 +40,7 @@ pub fn subscription() -> Subscription<Message> {
while let Some(request) = rx.recv().await { while let Some(request) = rx.recv().await {
if let Err(why) = request.write_to(&mut stream).await { if let Err(why) = request.write_to(&mut stream).await {
log::error!("error writing to GREETD_SOCK stream: {why}"); log::error!("error writing to GREETD_SOCK stream: {why:?}");
break; break;
} }
@ -85,8 +89,9 @@ pub fn subscription() -> Subscription<Message> {
"error while cancelling session: {}", "error while cancelling session: {}",
description description
); );
// Reconnect to socket // Reconnect to socket
_ = break break;
} }
_ => { _ => {
_ = sender.send(Message::Error(description)).await; _ = sender.send(Message::Error(description)).await;
@ -107,6 +112,7 @@ pub fn subscription() -> Subscription<Message> {
_ = sender.send(Message::Exit).await; _ = sender.send(Message::Exit).await;
} }
greetd_ipc::Request::CancelSession => { greetd_ipc::Request::CancelSession => {
log::info!("greetd IPC session canceled");
// Reconnect to socket // Reconnect to socket
break; break;
} }
@ -119,6 +125,9 @@ pub fn subscription() -> Subscription<Message> {
} }
} }
} }
log::info!("reconnecting to greetd IPC socket");
interval.tick().await;
} }
futures_util::future::pending().await futures_util::future::pending().await

View file

@ -7,9 +7,9 @@ use cosmic::iced::{Point, Rectangle, Size};
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings; use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
use cosmic::surface; use cosmic::surface;
use cosmic::{ use cosmic::{
executor, Element, executor,
iced::{ iced::{
self, alignment, self, Length, Subscription, alignment,
event::{ event::{
self, self,
wayland::{Event as WaylandEvent, OutputEvent, SessionLockEvent}, wayland::{Event as WaylandEvent, OutputEvent, SessionLockEvent},
@ -18,12 +18,12 @@ use cosmic::{
platform_specific::shell::wayland::commands::session_lock::{ platform_specific::shell::wayland::commands::session_lock::{
destroy_lock_surface, get_lock_surface, lock, unlock, destroy_lock_surface, get_lock_surface, lock, unlock,
}, },
Length, Subscription,
}, },
iced_runtime::core::window::Id as SurfaceId, iced_runtime::core::window::Id as SurfaceId,
style, widget, Element, style, widget,
}; };
use cosmic_config::CosmicConfigEntry; use cosmic_config::CosmicConfigEntry;
use std::time::Duration;
use std::{ use std::{
any::TypeId, any::TypeId,
collections::HashMap, collections::HashMap,
@ -35,8 +35,8 @@ use std::{
process, process,
sync::Arc, sync::Arc,
}; };
use tokio::{sync::mpsc, task, time}; use tokio::{sync::mpsc, task};
use wayland_client::{protocol::wl_output::WlOutput, Proxy}; use wayland_client::{Proxy, protocol::wl_output::WlOutput};
fn lockfile_opt() -> Option<PathBuf> { fn lockfile_opt() -> Option<PathBuf> {
let runtime_dir = dirs::runtime_dir()?; let runtime_dir = dirs::runtime_dir()?;
@ -113,7 +113,7 @@ pub fn pam_thread(username: String, conversation: Conversation) -> Result<(), pa
} }
pub struct Conversation { pub struct Conversation {
msg_tx: futures::channel::mpsc::Sender<Message>, msg_tx: futures::channel::mpsc::Sender<cosmic::Action<Message>>,
value_rx: mpsc::Receiver<String>, value_rx: mpsc::Receiver<String>,
} }
@ -127,24 +127,20 @@ impl Conversation {
log::error!("failed to convert prompt to UTF-8: {:?}", err); log::error!("failed to convert prompt to UTF-8: {:?}", err);
pam_client::ErrorCode::CONV_ERR pam_client::ErrorCode::CONV_ERR
})?; })?;
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all() futures::executor::block_on(async {
.build() self.msg_tx
.unwrap(); .send(cosmic::Action::App(Message::Prompt(
runtime prompt.to_string(),
.block_on(async { secret,
self.msg_tx Some(String::new()),
.send(Message::Prompt( )))
prompt.to_string(), .await
secret, })
Some(String::new()), .map_err(|err| {
)) log::error!("failed to send prompt: {:?}", err);
.await pam_client::ErrorCode::CONV_ERR
}) })?;
.map_err(|err| {
log::error!("failed to send prompt: {:?}", err);
pam_client::ErrorCode::CONV_ERR
})?;
let value = self.value_rx.blocking_recv().ok_or_else(|| { let value = self.value_rx.blocking_recv().ok_or_else(|| {
log::error!("failed to receive value: channel closed"); log::error!("failed to receive value: channel closed");
@ -165,7 +161,11 @@ impl Conversation {
futures::executor::block_on(async { futures::executor::block_on(async {
self.msg_tx self.msg_tx
.send(Message::Prompt(prompt.to_string(), false, None)) .send(cosmic::Action::App(Message::Prompt(
prompt.to_string(),
false,
None,
)))
.await .await
}) })
.map_err(|err| { .map_err(|err| {
@ -238,11 +238,23 @@ pub enum Message {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum State { enum State {
Locking, Locking,
Locked, Locked {
task_handle: cosmic::iced::task::Handle,
},
Unlocking, Unlocking,
Unlocked, Unlocked,
} }
impl Drop for State {
fn drop(&mut self) {
// Abort the locked task when the state is changed.
if let Self::Locked { task_handle } = self {
log::info!("dropping lockscreen tasks");
task_handle.abort();
}
}
}
/// The [`App`] stores application-specific state. /// The [`App`] stores application-specific state.
pub struct App { pub struct App {
core: Core, core: Core,
@ -264,7 +276,7 @@ pub struct App {
} }
impl App { impl App {
fn menu<'a>(&'a self, surface_id: SurfaceId) -> Element<'a, Message> { fn menu(&self, surface_id: SurfaceId) -> Element<Message> {
let left_element = { let left_element = {
let date_time_column = { let date_time_column = {
let mut column = widget::column::with_capacity(2).padding(16.0); let mut column = widget::column::with_capacity(2).padding(16.0);
@ -389,7 +401,7 @@ impl App {
) )
.id(text_input_id) .id(text_input_id)
.manage_value(true) .manage_value(true)
.on_submit(|v| Message::Submit(v)); .on_submit(Message::Submit);
if *secret { if *secret {
text_input = text_input.password() text_input = text_input.password()
@ -577,18 +589,18 @@ impl cosmic::Application for App {
let surface_id = SurfaceId::unique(); let surface_id = SurfaceId::unique();
let subsurface_id = SurfaceId::unique(); let subsurface_id = SurfaceId::unique();
match self.surface_ids.insert(output.clone(), surface_id) { if let Some(old_surface_id) =
Some(old_surface_id) => { self.surface_ids.insert(output.clone(), surface_id)
//TODO: remove old surface? {
log::warn!( //TODO: remove old surface?
"output {}: already had surface ID {:?}", log::warn!(
output.id(), "output {}: already had surface ID {:?}",
old_surface_id output.id(),
); old_surface_id
return Task::none(); );
} return Task::none();
None => {}
} }
let size = if let Some((w, h)) = let size = if let Some((w, h)) =
output_info_opt.as_ref().and_then(|info| info.logical_size) output_info_opt.as_ref().and_then(|info| info.logical_size)
{ {
@ -654,7 +666,7 @@ impl cosmic::Application for App {
})), })),
); );
if matches!(self.state, State::Locked) { if matches!(self.state, State::Locked { .. }) {
return Task::batch([ return Task::batch([
get_lock_surface(surface_id, output), get_lock_surface(surface_id, output),
cosmic::task::message(cosmic::Action::Cosmic( cosmic::task::message(cosmic::Action::Cosmic(
@ -672,7 +684,7 @@ impl cosmic::Application for App {
if let Some(n) = self.surface_names.remove(&surface_id) { if let Some(n) = self.surface_names.remove(&surface_id) {
self.text_input_ids.remove(&n); self.text_input_ids.remove(&n);
} }
if matches!(self.state, State::Locked) { if matches!(self.state, State::Locked { .. }) {
return destroy_lock_surface(surface_id); return destroy_lock_surface(surface_id);
} }
} }
@ -709,15 +721,89 @@ impl cosmic::Application for App {
SessionLockEvent::Focused(..) => {} SessionLockEvent::Focused(..) => {}
SessionLockEvent::Locked => { SessionLockEvent::Locked => {
log::info!("session locked"); log::info!("session locked");
if matches!(self.state, State::Locked) { if matches!(self.state, State::Locked { .. }) {
return Task::none(); return Task::none();
} }
self.state = State::Locked;
let username = self.flags.current_user.name.clone();
let (locked_task, locked_handle) = cosmic::task::stream(
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
// Send heartbeat once a second to update time.
let heartbeat_future = {
let mut output = msg_tx.clone();
async move {
let mut interval =
tokio::time::interval(Duration::from_secs(1));
loop {
output
.send(cosmic::Action::App(Message::None))
.await
.unwrap();
interval.tick().await;
}
}
};
let pam_future = async {
loop {
let (value_tx, value_rx) = mpsc::channel(16);
msg_tx
.send(cosmic::Action::App(Message::Channel(value_tx)))
.await
.unwrap();
let pam_res = {
let username = username.clone();
let msg_tx = msg_tx.clone();
task::spawn_blocking(move || {
pam_thread(username, Conversation { msg_tx, value_rx })
})
.await
.unwrap()
};
match pam_res {
Ok(()) => {
log::info!("successfully authenticated");
msg_tx
.send(cosmic::Action::App(Message::Unlock))
.await
.unwrap();
break;
}
Err(err) => {
log::warn!("authentication error: {}", err);
msg_tx
.send(cosmic::Action::App(Message::Error(
err.to_string(),
)))
.await
.unwrap();
}
}
}
};
futures::pin_mut!(heartbeat_future);
futures::pin_mut!(pam_future);
futures::future::select(heartbeat_future, pam_future).await;
}),
)
.abortable();
let mut commands = Vec::with_capacity(self.surface_ids.len() + 1);
commands.push(locked_task);
self.state = State::Locked {
task_handle: locked_handle,
};
// Allow suspend // Allow suspend
self.inhibit_opt = None; self.inhibit_opt = None;
// Create lock surfaces // Create lock surfaces
let mut commands = Vec::with_capacity(self.surface_ids.len());
for (output, surface_id) in self.surface_ids.iter() { for (output, surface_id) in self.surface_ids.iter() {
commands.push(get_lock_surface(*surface_id, output.clone())); commands.push(get_lock_surface(*surface_id, output.clone()));
@ -836,15 +922,11 @@ impl cosmic::Application for App {
}, },
Message::Suspend => { Message::Suspend => {
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
return cosmic::task::future(async move { return cosmic::Task::future(async move { crate::logind::suspend().await.err() })
match crate::logind::suspend().await { .and_then(|err| {
Ok(()) => cosmic::action::none(), log::error!("failed to suspend: {:?}", err);
Err(err) => { cosmic::task::message(cosmic::Action::App(Message::Error(err.to_string())))
log::error!("failed to suspend: {:?}", err); });
cosmic::Action::App(Message::Error(err.to_string()))
}
}
});
} }
Message::Error(error) => { Message::Error(error) => {
self.error_opt = Some(error); self.error_opt = Some(error);
@ -869,13 +951,13 @@ impl cosmic::Application for App {
State::Unlocking => { State::Unlocking => {
log::info!("session still unlocking"); log::info!("session still unlocking");
} }
State::Locking | State::Locked => { State::Locking | State::Locked { .. } => {
log::info!("session already locking or locked"); log::info!("session already locking or locked");
} }
}, },
Message::Unlock => { Message::Unlock => {
match self.state { match self.state {
State::Locked => { State::Locked { .. } => {
log::info!("sessing unlocking"); log::info!("sessing unlocking");
self.state = State::Unlocking; self.state = State::Unlocking;
// Clear errors // Clear errors
@ -891,6 +973,12 @@ impl cosmic::Application for App {
// Destroy lock surfaces // Destroy lock surfaces
let mut commands = Vec::with_capacity(self.surface_ids.len() + 1); let mut commands = Vec::with_capacity(self.surface_ids.len() + 1);
for (_output, surface_id) in self.surface_ids.iter() {
self.surface_names.remove(surface_id);
commands.push(destroy_lock_surface(*surface_id));
}
// Tell compositor to unlock // Tell compositor to unlock
commands.push(unlock()); commands.push(unlock());
@ -964,60 +1052,6 @@ impl cosmic::Application for App {
}), }),
); );
if matches!(self.state, State::Locked) {
struct HeartbeatSubscription;
subscriptions.push(Subscription::run_with_id(
TypeId::of::<HeartbeatSubscription>(),
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
loop {
// Send heartbeat once a second to update time
//TODO: only send this when needed
msg_tx.send(Message::None).await.unwrap();
time::sleep(time::Duration::new(1, 0)).await;
}
}),
));
struct PamSubscription;
//TODO: how to avoid cloning this on every time subscription is called?
let username = self.flags.current_user.name.clone();
subscriptions.push(Subscription::run_with_id(
TypeId::of::<PamSubscription>(),
cosmic::iced_futures::stream::channel(16, |mut msg_tx| async move {
loop {
let (value_tx, value_rx) = mpsc::channel(16);
msg_tx.send(Message::Channel(value_tx)).await.unwrap();
let pam_res = {
let username = username.clone();
let msg_tx = msg_tx.clone();
task::spawn_blocking(move || {
pam_thread(username, Conversation { msg_tx, value_rx })
})
.await
.unwrap()
};
match pam_res {
Ok(()) => {
log::info!("successfully authenticated");
msg_tx.send(Message::Unlock).await.unwrap();
break;
}
Err(err) => {
log::warn!("authentication error: {}", err);
msg_tx.send(Message::Error(err.to_string())).await.unwrap();
}
}
}
loop {
time::sleep(time::Duration::new(60, 0)).await;
}
}),
));
}
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
{ {
subscriptions.push(crate::logind::subscription()); subscriptions.push(crate::logind::subscription());
@ -1025,16 +1059,12 @@ impl cosmic::Application for App {
#[cfg(feature = "networkmanager")] #[cfg(feature = "networkmanager")]
{ {
subscriptions.push( subscriptions.push(crate::networkmanager::subscription().map(Message::NetworkIcon));
crate::networkmanager::subscription()
.map(|icon_opt| Message::NetworkIcon(icon_opt)),
);
} }
#[cfg(feature = "upower")] #[cfg(feature = "upower")]
{ {
subscriptions subscriptions.push(crate::upower::subscription().map(Message::PowerInfo));
.push(crate::upower::subscription().map(|info_opt| Message::PowerInfo(info_opt)));
} }
Subscription::batch(subscriptions) Subscription::batch(subscriptions)

View file

@ -1,12 +1,12 @@
use cosmic::iced::{ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
Subscription, Subscription,
futures::{SinkExt, StreamExt, channel::mpsc},
}; };
use logind_zbus::{ use logind_zbus::{
manager::{InhibitType, ManagerProxy}, manager::{InhibitType, ManagerProxy},
session::SessionProxy, session::SessionProxy,
}; };
use std::{any::TypeId, error::Error, os::fd::OwnedFd, sync::Arc}; use std::{any::TypeId, error::Error, os::fd::OwnedFd, sync::Arc, time::Duration};
use zbus::Connection; use zbus::Connection;
use crate::locker::Message; use crate::locker::Message;
@ -78,6 +78,9 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Message>) -> Result<(), Box<dyn E
let mut prepare_for_sleep = manager.receive_prepare_for_sleep().await?; let mut prepare_for_sleep = manager.receive_prepare_for_sleep().await?;
let mut lock = session.receive_lock().await?; let mut lock = session.receive_lock().await?;
let mut unlock = session.receive_unlock().await?; let mut unlock = session.receive_unlock().await?;
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop { loop {
// Waits until a signal has been received // Waits until a signal has been received
tokio::select!( tokio::select!(
@ -114,5 +117,7 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Message>) -> Result<(), Box<dyn E
log::info!("logind unlock"); log::info!("logind unlock");
msg_tx.send(Message::Unlock).await?; msg_tx.send(Message::Unlock).await?;
}); });
interval.tick().await;
} }
} }

View file

@ -1,10 +1,9 @@
use cosmic::iced::{ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
Subscription, Subscription,
futures::{SinkExt, StreamExt, channel::mpsc},
}; };
use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager}; use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager};
use std::{any::TypeId, cmp}; use std::{any::TypeId, cmp, time::Duration};
use tokio::time;
use zbus::{Connection, Result}; use zbus::{Connection, Result};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
@ -52,9 +51,7 @@ pub fn subscription() -> Subscription<Option<&'static str>> {
msg_tx.send(None).await.unwrap(); msg_tx.send(None).await.unwrap();
//TODO: should we retry on error? //TODO: should we retry on error?
loop { futures_util::future::pending().await
time::sleep(time::Duration::new(60, 0)).await;
}
}), }),
) )
} }
@ -63,8 +60,9 @@ pub fn subscription() -> Subscription<Option<&'static str>> {
pub async fn handler(msg_tx: &mut mpsc::Sender<Option<&'static str>>) -> Result<()> { 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?;
let mut active_conns_changed = nm.receive_active_connections_changed().await; let mut active_conns_changed = nm.receive_active_connections_changed().await;
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop { loop {
let mut icon = NetworkIcon::None; let mut icon = NetworkIcon::None;
@ -100,9 +98,7 @@ 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();
// Waits until active connections have changed and at least one second has passed // Waits until active connections have changed and at least one second has passed
tokio::join!( active_conns_changed.next().await;
active_conns_changed.next(), interval.tick().await;
time::sleep(time::Duration::from_secs(1))
);
} }
} }

View file

@ -1,9 +1,8 @@
use cosmic::iced::{ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
Subscription, Subscription,
futures::{SinkExt, StreamExt, channel::mpsc},
}; };
use std::any::TypeId; use std::{any::TypeId, time::Duration};
use tokio::time;
use upower_dbus::UPowerProxy; use upower_dbus::UPowerProxy;
use zbus::{Connection, Result}; use zbus::{Connection, Result};
@ -25,9 +24,7 @@ pub fn subscription() -> Subscription<Option<(String, f64)>> {
msg_tx.send(None).await.unwrap(); msg_tx.send(None).await.unwrap();
//TODO: should we retry on error? //TODO: should we retry on error?
loop { futures_util::future::pending().await
time::sleep(time::Duration::new(60, 0)).await;
}
}), }),
) )
} }
@ -40,6 +37,8 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Option<(String, f64)>>) -> Result
let mut icon_name_changed = dev.receive_icon_name_changed().await; let mut icon_name_changed = dev.receive_icon_name_changed().await;
let mut percentage_changed = dev.receive_percentage_changed().await; let mut percentage_changed = dev.receive_percentage_changed().await;
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop { loop {
let mut info_opt = None; let mut info_opt = None;
@ -53,7 +52,8 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Option<(String, f64)>>) -> Result
msg_tx.send(info_opt).await.unwrap(); msg_tx.send(info_opt).await.unwrap();
// Waits until icon or percentage have changed // Waits until icon or percentage have changed, and at least one second has passed.
tokio::select!(_ = icon_name_changed.next() => (), _ = percentage_changed.next() => ()); futures_util::future::select(icon_name_changed.next(), percentage_changed.next()).await;
interval.tick().await;
} }
} }