mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Implement Gnome idle watcher
This commit is contained in:
parent
a758913d52
commit
11cfb7c54b
@ -71,7 +71,7 @@ fn main() -> Result<(), BoxedError> {
|
||||
);
|
||||
info!(
|
||||
"Window polling period: {} seconds",
|
||||
client.config.poll_time_idle.as_secs()
|
||||
client.config.poll_time_window.as_secs()
|
||||
);
|
||||
|
||||
let mut thread_handlers = Vec::new();
|
||||
|
@ -1,3 +1,5 @@
|
||||
mod gnome_idle;
|
||||
mod idle;
|
||||
mod kwin_window;
|
||||
mod wl_bindings;
|
||||
mod wl_connection;
|
||||
@ -49,6 +51,7 @@ macro_rules! watcher {
|
||||
pub const IDLE: &WatcherConstructors = &[
|
||||
watcher!(wl_kwin_idle::IdleWatcher),
|
||||
watcher!(x11_screensaver_idle::IdleWatcher),
|
||||
watcher!(gnome_idle::IdleWatcher),
|
||||
];
|
||||
|
||||
pub const ACTIVE_WINDOW: &WatcherConstructors = &[
|
||||
|
49
src/watchers/gnome_idle.rs
Normal file
49
src/watchers/gnome_idle.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use super::{idle, Watcher};
|
||||
use crate::{report_client::ReportClient, BoxedError};
|
||||
use std::{sync::Arc, thread};
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
pub struct IdleWatcher {
|
||||
dbus_connection: Connection,
|
||||
}
|
||||
|
||||
impl idle::SinceLastInput for IdleWatcher {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError> {
|
||||
let ms = self
|
||||
.dbus_connection
|
||||
.call_method(
|
||||
Some("org.gnome.Mutter.IdleMonitor"),
|
||||
"/org/gnome/Mutter/IdleMonitor/Core",
|
||||
Some("org.gnome.Mutter.IdleMonitor"),
|
||||
"GetIdletime",
|
||||
&(),
|
||||
)?
|
||||
.body::<u64>()?;
|
||||
u32::try_from(ms / 1000).map_err(|_| format!("Number {ms} is invalid").into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn new() -> Result<Self, crate::BoxedError> {
|
||||
let watcher = Self {
|
||||
dbus_connection: Connection::session()?,
|
||||
};
|
||||
idle::SinceLastInput::seconds_since_input(&watcher)?;
|
||||
|
||||
Ok(watcher)
|
||||
}
|
||||
|
||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
||||
let mut is_idle = false;
|
||||
info!("Starting idle watcher");
|
||||
loop {
|
||||
match idle::ping_since_last_input(self, is_idle, client) {
|
||||
Ok(is_idle_again) => {
|
||||
is_idle = is_idle_again;
|
||||
}
|
||||
Err(e) => error!("Error on idle iteration: {e}"),
|
||||
};
|
||||
thread::sleep(client.config.poll_time_idle);
|
||||
}
|
||||
}
|
||||
}
|
49
src/watchers/idle.rs
Normal file
49
src/watchers/idle.rs
Normal file
@ -0,0 +1,49 @@
|
||||
use crate::{report_client::ReportClient, BoxedError};
|
||||
use chrono::{Duration, Utc};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait SinceLastInput {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError>;
|
||||
}
|
||||
|
||||
pub fn ping_since_last_input(
|
||||
watcher: &impl SinceLastInput,
|
||||
is_idle: bool,
|
||||
client: &Arc<ReportClient>,
|
||||
) -> Result<bool, BoxedError> {
|
||||
// The logic is rewritten from the original Python code:
|
||||
// https://github.com/ActivityWatch/aw-watcher-afk/blob/ef531605cd8238e00138bbb980e5457054e05248/aw_watcher_afk/afk.py#L73
|
||||
let duration_1ms: Duration = Duration::milliseconds(1);
|
||||
let duration_zero: Duration = Duration::zero();
|
||||
|
||||
let seconds_since_input = watcher.seconds_since_input()?;
|
||||
let now = Utc::now();
|
||||
let time_since_input = Duration::seconds(i64::from(seconds_since_input));
|
||||
let last_input = now - time_since_input;
|
||||
let mut is_idle_again = is_idle;
|
||||
|
||||
if is_idle && u64::from(seconds_since_input) < client.config.idle_timeout.as_secs() {
|
||||
debug!("No longer idle");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
is_idle_again = false;
|
||||
// ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
|
||||
client.ping(is_idle, last_input + duration_1ms, duration_zero)?;
|
||||
} else if !is_idle && u64::from(seconds_since_input) >= client.config.idle_timeout.as_secs() {
|
||||
debug!("Idle again");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
is_idle_again = true;
|
||||
// ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
|
||||
client.ping(is_idle, last_input + duration_1ms, time_since_input)?;
|
||||
} else {
|
||||
// Send a heartbeat if no state change was made
|
||||
if is_idle {
|
||||
trace!("Reporting as idle");
|
||||
client.ping(is_idle, last_input, time_since_input)?;
|
||||
} else {
|
||||
trace!("Reporting as not idle");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(is_idle_again)
|
||||
}
|
@ -1,51 +1,15 @@
|
||||
use std::{sync::Arc, thread};
|
||||
|
||||
use super::{x11_connection::X11Connection, BoxedError, Watcher};
|
||||
use super::{idle, x11_connection::X11Connection, BoxedError, Watcher};
|
||||
use crate::report_client::ReportClient;
|
||||
use chrono::{Duration, Utc};
|
||||
|
||||
pub struct IdleWatcher {
|
||||
connection: X11Connection,
|
||||
}
|
||||
|
||||
impl IdleWatcher {
|
||||
fn run(&self, is_idle: bool, client: &Arc<ReportClient>) -> Result<bool, BoxedError> {
|
||||
// The logic is rewritten from the original Python code:
|
||||
// https://github.com/ActivityWatch/aw-watcher-afk/blob/ef531605cd8238e00138bbb980e5457054e05248/aw_watcher_afk/afk.py#L73
|
||||
let duration_1ms: Duration = Duration::milliseconds(1);
|
||||
let duration_zero: Duration = Duration::zero();
|
||||
|
||||
let seconds_since_input = self.connection.seconds_since_last_input()?;
|
||||
let now = Utc::now();
|
||||
let time_since_input = Duration::seconds(i64::from(seconds_since_input));
|
||||
let last_input = now - time_since_input;
|
||||
let mut is_idle_again = is_idle;
|
||||
|
||||
if is_idle && u64::from(seconds_since_input) < client.config.idle_timeout.as_secs() {
|
||||
debug!("No longer idle");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
is_idle_again = false;
|
||||
// ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
|
||||
client.ping(is_idle, last_input + duration_1ms, duration_zero)?;
|
||||
} else if !is_idle && u64::from(seconds_since_input) >= client.config.idle_timeout.as_secs()
|
||||
{
|
||||
debug!("Idle again");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
is_idle_again = true;
|
||||
// ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
|
||||
client.ping(is_idle, last_input + duration_1ms, time_since_input)?;
|
||||
} else {
|
||||
// Send a heartbeat if no state change was made
|
||||
if is_idle {
|
||||
trace!("Reporting as idle");
|
||||
client.ping(is_idle, last_input, time_since_input)?;
|
||||
} else {
|
||||
trace!("Reporting as not idle");
|
||||
client.ping(is_idle, last_input, duration_zero)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(is_idle_again)
|
||||
impl idle::SinceLastInput for IdleWatcher {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError> {
|
||||
self.connection.seconds_since_last_input()
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +27,7 @@ impl Watcher for IdleWatcher {
|
||||
info!("Starting idle watcher");
|
||||
let mut is_idle = false;
|
||||
loop {
|
||||
match self.run(is_idle, client) {
|
||||
match idle::ping_since_last_input(self, is_idle, client) {
|
||||
Ok(is_idle_again) => {
|
||||
is_idle = is_idle_again;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user