Lock on suspend with inhibit

This commit is contained in:
Jeremy Soller 2024-04-05 19:02:20 -06:00
parent eb3ba78e57
commit 9d4fe5bb0b
2 changed files with 73 additions and 15 deletions

View file

@ -24,8 +24,10 @@ use std::{
collections::HashMap, collections::HashMap,
ffi::{CStr, CString}, ffi::{CStr, CString},
fs, fs,
os::fd::OwnedFd,
path::Path, path::Path,
process, process,
sync::Arc,
}; };
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 +197,7 @@ pub enum Message {
SessionLockEvent(SessionLockEvent), SessionLockEvent(SessionLockEvent),
Channel(mpsc::Sender<String>), Channel(mpsc::Sender<String>),
BackgroundState(cosmic_bg_config::state::State), BackgroundState(cosmic_bg_config::state::State),
Inhibit(Arc<OwnedFd>),
NetworkIcon(Option<&'static str>), NetworkIcon(Option<&'static str>),
PowerInfo(Option<(String, f64)>), PowerInfo(Option<(String, f64)>),
Prompt(String, bool, Option<String>), Prompt(String, bool, Option<String>),
@ -223,6 +226,7 @@ pub struct App {
surface_images: HashMap<SurfaceId, widget::image::Handle>, surface_images: HashMap<SurfaceId, widget::image::Handle>,
surface_names: HashMap<SurfaceId, String>, surface_names: HashMap<SurfaceId, String>,
text_input_ids: HashMap<SurfaceId, widget::Id>, text_input_ids: HashMap<SurfaceId, widget::Id>,
inhibit_opt: Option<Arc<OwnedFd>>,
network_icon_opt: Option<&'static str>, network_icon_opt: Option<&'static str>,
power_info_opt: Option<(String, f64)>, power_info_opt: Option<(String, f64)>,
value_tx_opt: Option<mpsc::Sender<String>>, value_tx_opt: Option<mpsc::Sender<String>>,
@ -318,6 +322,7 @@ impl cosmic::Application for App {
surface_images: HashMap::new(), surface_images: HashMap::new(),
surface_names: HashMap::new(), surface_names: HashMap::new(),
text_input_ids: HashMap::new(), text_input_ids: HashMap::new(),
inhibit_opt: None,
network_icon_opt: None, network_icon_opt: None,
power_info_opt: None, power_info_opt: None,
value_tx_opt: None, value_tx_opt: None,
@ -415,6 +420,8 @@ impl cosmic::Application for App {
SessionLockEvent::Locked => { SessionLockEvent::Locked => {
log::info!("session locked"); log::info!("session locked");
self.state = State::Locked; self.state = State::Locked;
// Allow suspend
self.inhibit_opt = None;
let mut commands = Vec::with_capacity(self.surface_ids.len()); 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()));
@ -443,6 +450,9 @@ impl cosmic::Application for App {
self.surface_images.clear(); self.surface_images.clear();
self.update_wallpapers(); self.update_wallpapers();
} }
Message::Inhibit(inhibit) => {
self.inhibit_opt = Some(inhibit);
}
Message::NetworkIcon(network_icon_opt) => { Message::NetworkIcon(network_icon_opt) => {
self.network_icon_opt = network_icon_opt; self.network_icon_opt = network_icon_opt;
} }
@ -813,13 +823,7 @@ impl cosmic::Application for App {
#[cfg(feature = "logind")] #[cfg(feature = "logind")]
{ {
subscriptions.push(crate::logind::subscription().map(|lock| { subscriptions.push(crate::logind::subscription());
if lock {
Message::Lock
} else {
Message::Unlock
}
}));
} }
#[cfg(feature = "networkmanager")] #[cfg(feature = "networkmanager")]

View file

@ -2,11 +2,22 @@ use cosmic::iced::{
futures::{channel::mpsc, SinkExt, StreamExt}, futures::{channel::mpsc, SinkExt, StreamExt},
subscription, Subscription, subscription, Subscription,
}; };
use logind_zbus::{manager::ManagerProxy, session::SessionProxy}; use logind_zbus::{
use std::{any::TypeId, error::Error, process}; manager::{InhibitType, ManagerProxy},
session::SessionProxy,
};
use std::{
any::TypeId,
error::Error,
os::fd::{FromRawFd, IntoRawFd, OwnedFd},
process,
sync::Arc,
};
use tokio::time; use tokio::time;
use zbus::Connection; use zbus::Connection;
use crate::locker::Message;
pub async fn power_off() -> zbus::Result<()> { pub async fn power_off() -> zbus::Result<()> {
let connection = Connection::system().await?; let connection = Connection::system().await?;
let manager = ManagerProxy::new(&connection).await?; let manager = ManagerProxy::new(&connection).await?;
@ -25,7 +36,21 @@ pub async fn suspend() -> zbus::Result<()> {
manager.suspend(false).await manager.suspend(false).await
} }
pub fn subscription() -> Subscription<bool> { async fn inhibit(manager: &ManagerProxy<'_>) -> zbus::Result<OwnedFd> {
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<Message> {
struct LogindSubscription; struct LogindSubscription;
subscription::channel( subscription::channel(
@ -49,7 +74,7 @@ pub fn subscription() -> Subscription<bool> {
} }
//TODO: use never type? //TODO: use never type?
pub async fn handler(msg_tx: &mut mpsc::Sender<bool>) -> Result<(), Box<dyn Error>> { pub async fn handler(msg_tx: &mut mpsc::Sender<Message>) -> Result<(), Box<dyn Error>> {
let connection = Connection::system().await?; let connection = Connection::system().await?;
let manager = ManagerProxy::new(&connection).await?; let manager = ManagerProxy::new(&connection).await?;
let session_path = manager.get_session_by_PID(process::id()).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<bool>) -> Result<(), Box<dyn Erro
.build() .build()
.await?; .await?;
let mut inhibit_opt = Some(inhibit(&manager).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?;
loop { loop {
// Waits until lock or unlock signals have been received // Waits until a signal has been received
tokio::select!(_ = lock.next() => { 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"); log::info!("logind lock");
msg_tx.send(true).await?; msg_tx.send(Message::Lock).await?;
}, _ = unlock.next() => { }, _ = unlock.next() => {
log::info!("logind unlock"); log::info!("logind unlock");
msg_tx.send(false).await?; msg_tx.send(Message::Unlock).await?;
}); });
} }
} }