Initial import. Most things seem working.

This includes an abortive attempt to do a gtk4 dialog (which I don't
think is possible, as gtk4 doesn't allow embedding toplevels anymore),
and an iced dialog, which I just never started writing.
This commit is contained in:
2022-05-03 17:05:06 -07:00
commit 2e86445c3d
29 changed files with 4597 additions and 0 deletions

18
systemd/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "bscreensaver-systemd"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1"
async-std = { version = "1.11", features = ["attributes"] }
async-xcb = { path = "../async-xcb" }
bscreensaver-command = { path = "../command" }
bscreensaver-util = { path = "../util" }
futures = "0.3"
log = "0.4"
nix = "0.23"
# git source needed until extension event error resolution fix is released
xcb = { git = "https://github.com/rust-x-bindings/rust-xcb", rev = "d09b5f91bc07d56673f1bc0d6c7ecd72b5ff7b3e" }
zbus = "2.2"
logind-zbus = "3"

99
systemd/src/main.rs Normal file
View File

@ -0,0 +1,99 @@
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};
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)
})?;
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?)
}