cosmic-idle/src/freedesktop_screensaver.rs
Vukašin Vojinović 983d34ad96 chore: update dependencies
Also migrates to Rust 2024 edition.
2025-11-13 14:59:24 -07:00

116 lines
3.8 KiB
Rust

// https://specifications.freedesktop.org/idle-inhibit-spec/latest
// https://invent.kde.org/plasma/kscreenlocker/-/blob/master/dbus/org.freedesktop.ScreenSaver.xml
use futures_lite::StreamExt;
use std::sync::{
Arc, Mutex,
atomic::{AtomicU32, Ordering},
};
use crate::{Event, EventSender};
#[derive(Debug)]
pub struct Inhibitor {
cookie: u32,
application_name: String,
reason_for_inhibit: String,
client: zbus::names::UniqueName<'static>,
}
#[derive(Clone)]
struct Screensaver {
inhibitors: Arc<Mutex<Vec<Inhibitor>>>,
last_cookie: Arc<AtomicU32>,
event_sender: EventSender,
}
#[zbus::interface(name = "org.freedesktop.ScreenSaver")]
impl Screensaver {
fn inhibit(
&mut self,
application_name: String,
reason_for_inhibit: String,
#[zbus(header)] header: zbus::message::Header<'_>,
) -> u32 {
let cookie = self.last_cookie.fetch_add(1, Ordering::Relaxed) + 1;
if let Some(sender) = header.sender() {
log::info!(
"Added screensaver inhibitor for application '{}' {:?}, reason: {}, cookie: {}",
application_name,
sender,
reason_for_inhibit,
cookie
);
let mut inhibitors = self.inhibitors.lock().unwrap();
if inhibitors.is_empty() {
let _ = self.event_sender.send(Event::ScreensaverInhibit(true));
}
inhibitors.push(Inhibitor {
cookie,
application_name,
reason_for_inhibit,
client: sender.to_owned(),
});
}
cookie
}
fn un_inhibit(&mut self, cookie: u32) {
let mut inhibitors = self.inhibitors.lock().unwrap();
if let Some(idx) = inhibitors.iter().position(|x| x.cookie == cookie) {
let inhibitor = inhibitors.remove(idx);
if inhibitors.is_empty() {
let _ = self.event_sender.send(Event::ScreensaverInhibit(false));
}
log::info!(
"Removed screensaver inhibitor for application '{}' {:?}, reason: {}, cookie: {}",
inhibitor.application_name,
inhibitor.client,
inhibitor.reason_for_inhibit,
inhibitor.cookie
);
}
}
}
pub async fn serve(event_sender: EventSender) -> zbus::Result<()> {
let inhibitors = Arc::new(Mutex::new(Vec::new()));
let screensaver = Screensaver {
inhibitors: inhibitors.clone(),
event_sender: event_sender.clone(),
last_cookie: Arc::new(AtomicU32::new(0)),
};
// Clients vary in which path they use
let conn = zbus::connection::Builder::session()?
.serve_at("/ScreenSaver", screensaver.clone())?
.serve_at("/org/freedesktop/ScreenSaver", screensaver)?
.build()
.await?;
conn.request_name_with_flags(
"org.freedesktop.ScreenSaver",
zbus::fdo::RequestNameFlags::ReplaceExisting.into(),
)
.await?;
// If a client disconnects from DBus, remove any inhibitors it has added.
let dbus = zbus::fdo::DBusProxy::new(&conn).await?;
let mut name_owner_stream = dbus.receive_name_owner_changed().await?;
while let Some(event) = name_owner_stream.next().await {
let args = event.args()?;
if args.new_owner.is_none()
&& let zbus::names::BusName::Unique(name) = args.name
{
let mut inhibitors = inhibitors.lock().unwrap();
if !inhibitors.is_empty() {
inhibitors.retain(|inhibitor| inhibitor.client != name);
if inhibitors.is_empty() {
let _ = event_sender.send(Event::ScreensaverInhibit(false));
}
}
}
}
Ok(())
}