bscreensaver/systemd/src/main.rs

100 lines
3.6 KiB
Rust

use async_std::task;
use futures::{future::FutureExt, pin_mut, select, AsyncReadExt, StreamExt};
use log::{debug, error, info, warn};
use logind_zbus::manager::{InhibitType, ManagerProxy};
use std::{os::unix::io::AsRawFd, process::exit, time::Duration};
use zbus::Connection;
use bscreensaver_command::{bscreensaver_command, BCommand};
use bscreensaver_util::init_logging;
#[async_std::main]
async fn main() {
init_logging("BSCREENSAVER_SYSTEMD_LOG");
let xcb_handle = task::spawn(xcb_task()).fuse();
let dbus_handle = task::spawn(dbus_task()).fuse();
pin_mut!(xcb_handle, dbus_handle);
let res = loop {
select! {
_ = xcb_handle => {
info!("Lost connection to X server; quitting");
break Ok(());
},
res = dbus_handle => {
match res {
Err(err) => error!("Lost connection to the session bus: {}", err),
Ok(_) => error!("DBus task exited normally; this should not happen!"),
}
break Err(());
},
};
};
if let Err(_) = res {
exit(1);
}
}
async fn xcb_task() -> anyhow::Result<()> {
let (xcb_conn, _) = task::block_on(async { xcb::Connection::connect(None) })?;
let mut xcb_conn = async_xcb::AsyncConnection::new(xcb_conn)?;
// We need to drain the XCB connection periodically. Even though we have not
// asked for any events, we'll still get stuff like MappingNotify if the keyboard
// settings change.
loop {
let mut buf = [0u8; 512];
xcb_conn.read(&mut buf).await?;
}
}
async fn dbus_task() -> anyhow::Result<()> {
let system_bus = Connection::system().await?;
let manager_proxy = ManagerProxy::new(&system_bus).await?;
let mut prepare_for_sleep_stream = manager_proxy.receive_prepare_for_sleep().await?;
let mut inhibit_fd = Some(register_sleep_lock(&manager_proxy).await?);
loop {
if let Some(prepare_for_sleep) = prepare_for_sleep_stream.next().await {
if *prepare_for_sleep.args()?.start() {
debug!("Preparing for sleep");
if let Err(err) = do_bscreensaver_command(BCommand::Lock).await {
warn!("Failed to lock screen: {}", err);
}
if let Some(fd) = inhibit_fd.take() {
if let Err(err) = nix::unistd::close(fd.as_raw_fd()) {
warn!("Failed to close sleep inhibit lock: {}", err);
}
} else {
warn!("No sleep lock present");
}
} else {
debug!("Resuming from sleep");
if let Err(err) = do_bscreensaver_command(BCommand::Deactivate).await {
warn!("Failed to deactivate screen lock: {}", err);
}
inhibit_fd = Some(register_sleep_lock(&manager_proxy).await?);
}
}
}
}
async fn do_bscreensaver_command(command: BCommand) -> anyhow::Result<()> {
task::block_on(async {
bscreensaver_command(command, Some(Duration::from_secs(4)))
})?;
Ok(())
}
async fn register_sleep_lock<'a>(manager_proxy: &ManagerProxy<'a>) -> anyhow::Result<zbus::zvariant::OwnedFd> {
debug!("Registering sleep lock");
// ManagerProxy uses RawFd for the return value, which rust's type system thinks is an i32,
// which means the generated proxy uses the wrong dbus type signature. So instead, use a raw
// Proxy instance and do it all ourselves.
Ok((*manager_proxy).call("Inhibit", &(InhibitType::Sleep, "bscreensaver", "blank before sleep", "delay")).await?)
}