100 lines
3.6 KiB
Rust
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?)
|
|
}
|