From 9d4fe5bb0b3c5c332c568e85167cc49860925134 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 5 Apr 2024 19:02:20 -0600 Subject: [PATCH] Lock on suspend with inhibit --- src/locker.rs | 18 +++++++------ src/logind.rs | 70 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/locker.rs b/src/locker.rs index 8297317..06966be 100644 --- a/src/locker.rs +++ b/src/locker.rs @@ -24,8 +24,10 @@ use std::{ collections::HashMap, ffi::{CStr, CString}, fs, + os::fd::OwnedFd, path::Path, process, + sync::Arc, }; use tokio::{sync::mpsc, task, time}; use wayland_client::{protocol::wl_output::WlOutput, Proxy}; @@ -195,6 +197,7 @@ pub enum Message { SessionLockEvent(SessionLockEvent), Channel(mpsc::Sender), BackgroundState(cosmic_bg_config::state::State), + Inhibit(Arc), NetworkIcon(Option<&'static str>), PowerInfo(Option<(String, f64)>), Prompt(String, bool, Option), @@ -223,6 +226,7 @@ pub struct App { surface_images: HashMap, surface_names: HashMap, text_input_ids: HashMap, + inhibit_opt: Option>, network_icon_opt: Option<&'static str>, power_info_opt: Option<(String, f64)>, value_tx_opt: Option>, @@ -318,6 +322,7 @@ impl cosmic::Application for App { surface_images: HashMap::new(), surface_names: HashMap::new(), text_input_ids: HashMap::new(), + inhibit_opt: None, network_icon_opt: None, power_info_opt: None, value_tx_opt: None, @@ -415,6 +420,8 @@ impl cosmic::Application for App { SessionLockEvent::Locked => { log::info!("session locked"); self.state = State::Locked; + // Allow suspend + self.inhibit_opt = None; 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())); @@ -443,6 +450,9 @@ impl cosmic::Application for App { self.surface_images.clear(); self.update_wallpapers(); } + Message::Inhibit(inhibit) => { + self.inhibit_opt = Some(inhibit); + } Message::NetworkIcon(network_icon_opt) => { self.network_icon_opt = network_icon_opt; } @@ -813,13 +823,7 @@ impl cosmic::Application for App { #[cfg(feature = "logind")] { - subscriptions.push(crate::logind::subscription().map(|lock| { - if lock { - Message::Lock - } else { - Message::Unlock - } - })); + subscriptions.push(crate::logind::subscription()); } #[cfg(feature = "networkmanager")] diff --git a/src/logind.rs b/src/logind.rs index b83a88b..26ca923 100644 --- a/src/logind.rs +++ b/src/logind.rs @@ -2,11 +2,22 @@ use cosmic::iced::{ futures::{channel::mpsc, SinkExt, StreamExt}, subscription, Subscription, }; -use logind_zbus::{manager::ManagerProxy, session::SessionProxy}; -use std::{any::TypeId, error::Error, process}; +use logind_zbus::{ + manager::{InhibitType, ManagerProxy}, + session::SessionProxy, +}; +use std::{ + any::TypeId, + error::Error, + os::fd::{FromRawFd, IntoRawFd, OwnedFd}, + process, + sync::Arc, +}; use tokio::time; use zbus::Connection; +use crate::locker::Message; + pub async fn power_off() -> zbus::Result<()> { let connection = Connection::system().await?; let manager = ManagerProxy::new(&connection).await?; @@ -25,7 +36,21 @@ pub async fn suspend() -> zbus::Result<()> { manager.suspend(false).await } -pub fn subscription() -> Subscription { +async fn inhibit(manager: &ManagerProxy<'_>) -> zbus::Result { + let what = InhibitType::Sleep; + let who = "COSMIC Greeter"; + let why = "COSMIC Greeter needs to display a lock screen"; + let mode = "delay"; + //TODO: update logind-zbus to fix inhibit signature + let fd: zbus::zvariant::OwnedFd = manager + .inner() + .call("Inhibit", &(what, who, why, mode)) + .await?; + // Have to convert to std type to avoid leaking zbus dependency + Ok(unsafe { OwnedFd::from_raw_fd(fd.into_raw_fd()) }) +} + +pub fn subscription() -> Subscription { struct LogindSubscription; subscription::channel( @@ -49,7 +74,7 @@ pub fn subscription() -> Subscription { } //TODO: use never type? -pub async fn handler(msg_tx: &mut mpsc::Sender) -> Result<(), Box> { +pub async fn handler(msg_tx: &mut mpsc::Sender) -> Result<(), Box> { let connection = Connection::system().await?; let manager = ManagerProxy::new(&connection).await?; let session_path = manager.get_session_by_PID(process::id()).await?; @@ -58,16 +83,45 @@ pub async fn handler(msg_tx: &mut mpsc::Sender) -> Result<(), Box { + // Waits until a signal has been received + tokio::select!( + signal_opt = prepare_for_sleep.next() => { + match signal_opt { + Some(signal) => match signal.args() { + Ok(args) => { + if args.start { + log::info!("logind prepare for sleep"); + if let Some(inhibit) = inhibit_opt.take() { + msg_tx.send(Message::Inhibit(Arc::new(inhibit))).await?; + } + msg_tx.send(Message::Lock).await?; + } else { + log::info!("logind resume"); + if inhibit_opt.is_none() { + inhibit_opt = Some(inhibit(&manager).await?); + } + } + }, + Err(err) => { + log::warn!("logind prepare to sleep invalid data: {}", err); + } + }, + None => { + log::warn!("logind prepare to sleep missing data"); + } + } + }, + _ = lock.next() => { log::info!("logind lock"); - msg_tx.send(true).await?; + msg_tx.send(Message::Lock).await?; }, _ = unlock.next() => { log::info!("logind unlock"); - msg_tx.send(false).await?; + msg_tx.send(Message::Unlock).await?; }); } }