mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-07 03:55:29 +00:00
Implement Gnome idle watcher
This commit is contained in:
parent
a758913d52
commit
11cfb7c54b
@ -71,7 +71,7 @@ fn main() -> Result<(), BoxedError> {
|
|||||||
);
|
);
|
||||||
info!(
|
info!(
|
||||||
"Window polling period: {} seconds",
|
"Window polling period: {} seconds",
|
||||||
client.config.poll_time_idle.as_secs()
|
client.config.poll_time_window.as_secs()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut thread_handlers = Vec::new();
|
let mut thread_handlers = Vec::new();
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
mod gnome_idle;
|
||||||
|
mod idle;
|
||||||
mod kwin_window;
|
mod kwin_window;
|
||||||
mod wl_bindings;
|
mod wl_bindings;
|
||||||
mod wl_connection;
|
mod wl_connection;
|
||||||
@ -49,6 +51,7 @@ macro_rules! watcher {
|
|||||||
pub const IDLE: &WatcherConstructors = &[
|
pub const IDLE: &WatcherConstructors = &[
|
||||||
watcher!(wl_kwin_idle::IdleWatcher),
|
watcher!(wl_kwin_idle::IdleWatcher),
|
||||||
watcher!(x11_screensaver_idle::IdleWatcher),
|
watcher!(x11_screensaver_idle::IdleWatcher),
|
||||||
|
watcher!(gnome_idle::IdleWatcher),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub const ACTIVE_WINDOW: &WatcherConstructors = &[
|
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 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 crate::report_client::ReportClient;
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
connection: X11Connection,
|
connection: X11Connection,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdleWatcher {
|
impl idle::SinceLastInput for IdleWatcher {
|
||||||
fn run(&self, is_idle: bool, client: &Arc<ReportClient>) -> Result<bool, BoxedError> {
|
fn seconds_since_input(&self) -> Result<u32, BoxedError> {
|
||||||
// The logic is rewritten from the original Python code:
|
self.connection.seconds_since_last_input()
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +27,7 @@ impl Watcher for IdleWatcher {
|
|||||||
info!("Starting idle watcher");
|
info!("Starting idle watcher");
|
||||||
let mut is_idle = false;
|
let mut is_idle = false;
|
||||||
loop {
|
loop {
|
||||||
match self.run(is_idle, client) {
|
match idle::ping_since_last_input(self, is_idle, client) {
|
||||||
Ok(is_idle_again) => {
|
Ok(is_idle_again) => {
|
||||||
is_idle = is_idle_again;
|
is_idle = is_idle_again;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user