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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,9 +7,9 @@ use cosmic::iced::{Point, Rectangle, Size};
use cosmic::iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings;
use cosmic::surface;
use cosmic::{
executor,
Element, executor,
iced::{
self, alignment,
self, Length, Subscription, alignment,
event::{
self,
wayland::{Event as WaylandEvent, OutputEvent, SessionLockEvent},
@ -18,12 +18,12 @@ use cosmic::{
platform_specific::shell::wayland::commands::session_lock::{
destroy_lock_surface, get_lock_surface, lock, unlock,
},
Length, Subscription,
},
iced_runtime::core::window::Id as SurfaceId,
style, widget, Element,
style, widget,
};
use cosmic_config::CosmicConfigEntry;
use std::time::Duration;
use std::{
any::TypeId,
collections::HashMap,
@ -35,8 +35,8 @@ use std::{
process,
sync::Arc,
};
use tokio::{sync::mpsc, task, time};
use wayland_client::{protocol::wl_output::WlOutput, Proxy};
use tokio::{sync::mpsc, task};
use wayland_client::{Proxy, protocol::wl_output::WlOutput};
fn lockfile_opt() -> Option<PathBuf> {
let runtime_dir = dirs::runtime_dir()?;
@ -113,7 +113,7 @@ pub fn pam_thread(username: String, conversation: Conversation) -> Result<(), pa
}
pub struct Conversation {
msg_tx: futures::channel::mpsc::Sender<Message>,
msg_tx: futures::channel::mpsc::Sender<cosmic::Action<Message>>,
value_rx: mpsc::Receiver<String>,
}
@ -127,18 +127,14 @@ impl Conversation {
log::error!("failed to convert prompt to UTF-8: {:?}", err);
pam_client::ErrorCode::CONV_ERR
})?;
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
runtime
.block_on(async {
futures::executor::block_on(async {
self.msg_tx
.send(Message::Prompt(
.send(cosmic::Action::App(Message::Prompt(
prompt.to_string(),
secret,
Some(String::new()),
))
)))
.await
})
.map_err(|err| {
@ -165,7 +161,11 @@ impl Conversation {
futures::executor::block_on(async {
self.msg_tx
.send(Message::Prompt(prompt.to_string(), false, None))
.send(cosmic::Action::App(Message::Prompt(
prompt.to_string(),
false,
None,
)))
.await
})
.map_err(|err| {
@ -238,11 +238,23 @@ pub enum Message {
#[derive(Clone, Debug)]
enum State {
Locking,
Locked,
Locked {
task_handle: cosmic::iced::task::Handle,
},
Unlocking,
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.
pub struct App {
core: Core,
@ -264,7 +276,7 @@ pub struct 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 date_time_column = {
let mut column = widget::column::with_capacity(2).padding(16.0);
@ -389,7 +401,7 @@ impl App {
)
.id(text_input_id)
.manage_value(true)
.on_submit(|v| Message::Submit(v));
.on_submit(Message::Submit);
if *secret {
text_input = text_input.password()
@ -577,8 +589,9 @@ impl cosmic::Application for App {
let surface_id = SurfaceId::unique();
let subsurface_id = SurfaceId::unique();
match self.surface_ids.insert(output.clone(), surface_id) {
Some(old_surface_id) => {
if let Some(old_surface_id) =
self.surface_ids.insert(output.clone(), surface_id)
{
//TODO: remove old surface?
log::warn!(
"output {}: already had surface ID {:?}",
@ -587,8 +600,7 @@ impl cosmic::Application for App {
);
return Task::none();
}
None => {}
}
let size = if let Some((w, h)) =
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([
get_lock_surface(surface_id, output),
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) {
self.text_input_ids.remove(&n);
}
if matches!(self.state, State::Locked) {
if matches!(self.state, State::Locked { .. }) {
return destroy_lock_surface(surface_id);
}
}
@ -709,15 +721,89 @@ impl cosmic::Application for App {
SessionLockEvent::Focused(..) => {}
SessionLockEvent::Locked => {
log::info!("session locked");
if matches!(self.state, State::Locked) {
if matches!(self.state, State::Locked { .. }) {
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
self.inhibit_opt = None;
// Create lock surfaces
let mut commands = Vec::with_capacity(self.surface_ids.len());
for (output, surface_id) in self.surface_ids.iter() {
commands.push(get_lock_surface(*surface_id, output.clone()));
@ -836,14 +922,10 @@ impl cosmic::Application for App {
},
Message::Suspend => {
#[cfg(feature = "logind")]
return cosmic::task::future(async move {
match crate::logind::suspend().await {
Ok(()) => cosmic::action::none(),
Err(err) => {
return cosmic::Task::future(async move { crate::logind::suspend().await.err() })
.and_then(|err| {
log::error!("failed to suspend: {:?}", err);
cosmic::Action::App(Message::Error(err.to_string()))
}
}
cosmic::task::message(cosmic::Action::App(Message::Error(err.to_string())))
});
}
Message::Error(error) => {
@ -869,13 +951,13 @@ impl cosmic::Application for App {
State::Unlocking => {
log::info!("session still unlocking");
}
State::Locking | State::Locked => {
State::Locking | State::Locked { .. } => {
log::info!("session already locking or locked");
}
},
Message::Unlock => {
match self.state {
State::Locked => {
State::Locked { .. } => {
log::info!("sessing unlocking");
self.state = State::Unlocking;
// Clear errors
@ -891,6 +973,12 @@ impl cosmic::Application for App {
// Destroy lock surfaces
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
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")]
{
subscriptions.push(crate::logind::subscription());
@ -1025,16 +1059,12 @@ impl cosmic::Application for App {
#[cfg(feature = "networkmanager")]
{
subscriptions.push(
crate::networkmanager::subscription()
.map(|icon_opt| Message::NetworkIcon(icon_opt)),
);
subscriptions.push(crate::networkmanager::subscription().map(Message::NetworkIcon));
}
#[cfg(feature = "upower")]
{
subscriptions
.push(crate::upower::subscription().map(|info_opt| Message::PowerInfo(info_opt)));
subscriptions.push(crate::upower::subscription().map(Message::PowerInfo));
}
Subscription::batch(subscriptions)

View file

@ -1,12 +1,12 @@
use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
Subscription,
futures::{SinkExt, StreamExt, channel::mpsc},
};
use logind_zbus::{
manager::{InhibitType, ManagerProxy},
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 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 lock = session.receive_lock().await?;
let mut unlock = session.receive_unlock().await?;
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
// Waits until a signal has been received
tokio::select!(
@ -114,5 +117,7 @@ pub async fn handler(msg_tx: &mut mpsc::Sender<Message>) -> Result<(), Box<dyn E
log::info!("logind unlock");
msg_tx.send(Message::Unlock).await?;
});
interval.tick().await;
}
}

View file

@ -1,10 +1,9 @@
use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt},
Subscription,
futures::{SinkExt, StreamExt, channel::mpsc},
};
use cosmic_dbus_networkmanager::{device::SpecificDevice, nm::NetworkManager};
use std::{any::TypeId, cmp};
use tokio::time;
use std::{any::TypeId, cmp, time::Duration};
use zbus::{Connection, Result};
#[derive(Clone, Copy, Debug)]
@ -52,9 +51,7 @@ pub fn subscription() -> Subscription<Option<&'static str>> {
msg_tx.send(None).await.unwrap();
//TODO: should we retry on error?
loop {
time::sleep(time::Duration::new(60, 0)).await;
}
futures_util::future::pending().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<()> {
let zbus = Connection::system().await?;
let nm = NetworkManager::new(&zbus).await?;
let mut active_conns_changed = nm.receive_active_connections_changed().await;
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
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();
// 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))
);
active_conns_changed.next().await;
interval.tick().await;
}
}

View file

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