From ddfe972cffcc27d1955511eb748e7f378b502984 Mon Sep 17 00:00:00 2001 From: Demmie <2e3s19@gmail.com> Date: Sat, 6 Jul 2024 23:39:48 -0400 Subject: [PATCH] Extract tracker service and fix reactive timings --- Cargo.lock | 72 --- watchers/Cargo.toml | 1 - watchers/src/lib.rs | 1 - watchers/src/report_client.rs | 30 +- watchers/src/subscriber.rs | 14 - watchers/src/watchers.rs | 2 +- watchers/src/watchers/gnome_idle.rs | 13 +- watchers/src/watchers/idle.rs | 412 ++++++++++++++---- watchers/src/watchers/wl_ext_idle_notify.rs | 26 +- watchers/src/watchers/wl_kwin_idle.rs | 28 +- watchers/src/watchers/x11_screensaver_idle.rs | 12 +- 11 files changed, 383 insertions(+), 228 deletions(-) delete mode 100644 watchers/src/subscriber.rs diff --git a/Cargo.lock b/Cargo.lock index edac2c8..eba0513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1084,12 +1084,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "downcast" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" - [[package]] name = "downcast-rs" version = "1.2.0" @@ -1338,12 +1332,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fragile" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" - [[package]] name = "futures" version = "0.3.28" @@ -2123,33 +2111,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "mockall" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48" -dependencies = [ - "cfg-if 1.0.0", - "downcast", - "fragile", - "lazy_static", - "mockall_derive", - "predicates", - "predicates-tree", -] - -[[package]] -name = "mockall_derive" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2" -dependencies = [ - "cfg-if 1.0.0", - "proc-macro2", - "quote", - "syn 2.0.64", -] - [[package]] name = "mpsc_requests" version = "0.3.3" @@ -2575,32 +2536,6 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "predicates" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" -dependencies = [ - "anstyle", - "predicates-core", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "proc-macro-crate" version = "3.1.0" @@ -3541,12 +3476,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "textwrap" version = "0.11.0" @@ -4111,7 +4040,6 @@ dependencies = [ "dirs 5.0.1", "gethostname", "log", - "mockall", "regex", "rstest", "serde", diff --git a/watchers/Cargo.toml b/watchers/Cargo.toml index 6bf6420..e4b5b41 100644 --- a/watchers/Cargo.toml +++ b/watchers/Cargo.toml @@ -12,7 +12,6 @@ path = "src/lib.rs" [dev-dependencies] rstest = "0.21.0" tempfile = "3.10.1" -mockall = "0.12.1" [dependencies] aw-client-rust = { git = "https://github.com/ActivityWatch/aw-server-rust", rev = "bb787fd" } diff --git a/watchers/src/lib.rs b/watchers/src/lib.rs index 1f6ef88..174d5ae 100644 --- a/watchers/src/lib.rs +++ b/watchers/src/lib.rs @@ -3,7 +3,6 @@ extern crate log; pub mod config; mod report_client; -mod subscriber; mod watchers; pub use crate::report_client::ReportClient; diff --git a/watchers/src/report_client.rs b/watchers/src/report_client.rs index 620efd8..153500d 100644 --- a/watchers/src/report_client.rs +++ b/watchers/src/report_client.rs @@ -1,8 +1,7 @@ -use crate::subscriber::IdleSubscriber; +use crate::watchers::idle::Status; use super::config::Config; use anyhow::Context; -use async_trait::async_trait; use aw_client_rust::{AwClient, Event as AwEvent}; use chrono::{DateTime, TimeDelta, Utc}; use serde_json::{Map, Value}; @@ -153,10 +152,21 @@ impl ReportClient { .await .with_context(|| format!("Failed to create bucket {bucket_name}")) } -} -#[async_trait] -impl IdleSubscriber for ReportClient { + pub async fn handle_idle_status(&self, status: Status) -> anyhow::Result<()> { + match status { + Status::Idle { + changed, + last_input_time, + duration, + } => self.idle(changed, last_input_time, duration).await, + Status::Active { + changed, + last_input_time, + } => self.non_idle(changed, last_input_time).await, + } + } + async fn idle( &self, changed: bool, @@ -191,14 +201,14 @@ impl IdleSubscriber for ReportClient { last_input_time.format("%Y-%m-%d %H:%M:%S") ); - self.ping(true, last_input_time, TimeDelta::zero()).await?; - self.ping( - false, - last_input_time + TimeDelta::milliseconds(1), + true, + last_input_time - TimeDelta::milliseconds(1), TimeDelta::zero(), ) - .await + .await?; + + self.ping(false, last_input_time, TimeDelta::zero()).await } else { trace!( "Reporting as not idle at {}", diff --git a/watchers/src/subscriber.rs b/watchers/src/subscriber.rs deleted file mode 100644 index 13b7e65..0000000 --- a/watchers/src/subscriber.rs +++ /dev/null @@ -1,14 +0,0 @@ -use async_trait::async_trait; -use chrono::{DateTime, TimeDelta, Utc}; - -#[async_trait] -pub trait IdleSubscriber: Sync + Send { - async fn idle( - &self, - changed: bool, - last_input_time: DateTime, - duration: TimeDelta, - ) -> anyhow::Result<()>; - - async fn non_idle(&self, changed: bool, last_input_time: DateTime) -> anyhow::Result<()>; -} diff --git a/watchers/src/watchers.rs b/watchers/src/watchers.rs index b99328d..26572e7 100644 --- a/watchers/src/watchers.rs +++ b/watchers/src/watchers.rs @@ -4,7 +4,7 @@ mod gnome_idle; mod gnome_wayland; #[cfg(feature = "gnome")] mod gnome_window; -mod idle; +pub mod idle; #[cfg(feature = "kwin_window")] mod kwin_window; mod wl_connection; diff --git a/watchers/src/watchers/gnome_idle.rs b/watchers/src/watchers/gnome_idle.rs index 8135789..f391dc0 100644 --- a/watchers/src/watchers/gnome_idle.rs +++ b/watchers/src/watchers/gnome_idle.rs @@ -2,12 +2,13 @@ use super::{gnome_wayland::load_watcher, idle, Watcher}; use crate::report_client::ReportClient; use anyhow::Context; use async_trait::async_trait; +use chrono::Utc; use std::sync::Arc; use zbus::Connection; pub struct IdleWatcher { dbus_connection: Connection, - idle_state: idle::State, + idle_state: idle::Tracker, } impl IdleWatcher { @@ -35,7 +36,7 @@ impl Watcher for IdleWatcher { load_watcher(|| async move { let mut watcher = Self { dbus_connection: Connection::session().await?, - idle_state: idle::State::new(duration, client.clone()), + idle_state: idle::Tracker::new(Utc::now(), duration), }; watcher.seconds_since_input().await?; Ok(watcher) @@ -43,10 +44,10 @@ impl Watcher for IdleWatcher { .await } - async fn run_iteration(&mut self, _: &Arc) -> anyhow::Result<()> { + async fn run_iteration(&mut self, client: &Arc) -> anyhow::Result<()> { let seconds = self.seconds_since_input().await?; - self.idle_state.send_with_last_input(seconds).await?; - - Ok(()) + client + .handle_idle_status(self.idle_state.get_with_last_input(Utc::now(), seconds)?) + .await } } diff --git a/watchers/src/watchers/idle.rs b/watchers/src/watchers/idle.rs index 55322de..e42929e 100644 --- a/watchers/src/watchers/idle.rs +++ b/watchers/src/watchers/idle.rs @@ -1,56 +1,63 @@ -use crate::subscriber::IdleSubscriber; use chrono::{DateTime, TimeDelta, Utc}; -use std::{cmp::max, sync::Arc}; +use std::cmp::max; -pub struct State { +pub struct Tracker { last_input_time: DateTime, - changed_time: DateTime, is_idle: bool, is_changed: bool, idle_timeout: TimeDelta, - subscriber: Arc, - idle_start: Option>, idle_end: Option>, } -impl State { - pub fn new(idle_timeout: TimeDelta, subscriber: Arc) -> Self { +pub enum Status { + Idle { + changed: bool, + last_input_time: DateTime, + duration: TimeDelta, + }, + Active { + changed: bool, + last_input_time: DateTime, + }, +} + +impl Tracker { + pub fn new(now: DateTime, idle_timeout: TimeDelta) -> Self { Self { - last_input_time: Utc::now(), - changed_time: Utc::now(), + last_input_time: now, is_idle: false, is_changed: false, idle_timeout, - idle_start: None, idle_end: None, - subscriber, } } - fn set_idle(&mut self, is_idle: bool, now: DateTime) { + fn set_idle(&mut self, is_idle: bool) { self.is_idle = is_idle; self.is_changed = true; - self.changed_time = now; } - pub fn mark_not_idle(&mut self) { - self.last_input_time = Utc::now(); - self.set_idle(false, self.last_input_time); + pub fn mark_not_idle(&mut self, now: DateTime) { + debug!("No longer idle"); + self.last_input_time = now; + self.set_idle(false); - self.idle_end = self.changed_time.into(); + self.idle_end = Some(now); } - pub fn mark_idle(&mut self) { - self.set_idle(true, Utc::now()); - - self.idle_start = self.changed_time.into(); + pub fn mark_idle(&mut self, _: DateTime) { + debug!("Idle again"); + self.set_idle(true); } // The logic is rewritten from the original Python code: // https://github.com/ActivityWatch/aw-watcher-afk/blob/ef531605cd8238e00138bbb980e5457054e05248/aw_watcher_afk/afk.py#L73 - pub async fn send_with_last_input(&mut self, seconds_since_input: u32) -> anyhow::Result<()> { - let now = Utc::now(); + pub fn get_with_last_input( + &mut self, + now: DateTime, + seconds_since_input: u32, + ) -> anyhow::Result { let time_since_input = TimeDelta::seconds(i64::from(seconds_since_input)); self.last_input_time = now - time_since_input; @@ -59,108 +66,341 @@ impl State { && u64::from(seconds_since_input) < self.idle_timeout.num_seconds().try_into().unwrap() { debug!("No longer idle"); - self.set_idle(false, now); + self.set_idle(false); } else if !self.is_idle && u64::from(seconds_since_input) >= self.idle_timeout.num_seconds().try_into().unwrap() { debug!("Idle again"); - self.set_idle(true, now); + self.set_idle(true); } - self.send_ping(now).await + Ok(self.get_status(now)) } - pub async fn send_reactive(&mut self) -> anyhow::Result<()> { - let now = Utc::now(); + pub fn get_reactive(&mut self, now: DateTime) -> anyhow::Result { if !self.is_idle { - self.last_input_time = max(now - self.idle_timeout, self.changed_time); - if let (Some(idle_start), Some(idle_end)) = (self.idle_start, self.idle_end) { - if !self.is_changed - && idle_start <= self.last_input_time - && self.last_input_time <= idle_end - { - warn!("Active time may not be accounted for."); - - // TODO: send the correct timings. - // After idle_end there is some active time for idle_timeout which may be accounted as idle time if it becomes idle soon. - return Ok(()); + self.last_input_time = max(self.last_input_time, now - self.idle_timeout); + + if let Some(idle_end) = self.idle_end { + if self.last_input_time < idle_end { + self.last_input_time = idle_end; } } } - self.send_ping(now).await + Ok(self.get_status(now)) } - async fn send_ping(&mut self, now: DateTime) -> anyhow::Result<()> { - if self.is_changed { + fn get_status(&mut self, now: DateTime) -> Status { + let result = if self.is_changed { if self.is_idle { - self.subscriber - .idle( - self.is_changed, - self.last_input_time, - now - self.last_input_time, - ) - .await?; + Status::Idle { + changed: self.is_changed, + last_input_time: self.last_input_time, + duration: now - self.last_input_time, + } } else { - self.subscriber - .non_idle(self.is_changed, self.last_input_time) - .await?; - }; + Status::Active { + changed: self.is_changed, + last_input_time: self.last_input_time, + } + } } else if self.is_idle { - self.subscriber - .idle( - self.is_changed, - self.last_input_time, - now - self.last_input_time, - ) - .await?; + Status::Idle { + changed: self.is_changed, + last_input_time: self.last_input_time, + duration: now - self.last_input_time, + } } else { - self.subscriber - .non_idle(self.is_changed, self.last_input_time) - .await?; - } + Status::Active { + changed: self.is_changed, + last_input_time: self.last_input_time, + } + }; self.is_changed = false; - Ok(()) + result } } #[cfg(test)] mod tests { use super::*; - use async_trait::async_trait; - use chrono::Duration; - use mockall::mock; + use chrono::{Duration, TimeZone}; use rstest::rstest; - mock! { - pub Subscriber {} - #[async_trait] - impl IdleSubscriber for Subscriber { - async fn idle(&self, changed: bool, last_input_time: DateTime, duration: TimeDelta) -> anyhow::Result<()>; - async fn non_idle(&self, changed: bool, last_input_time: DateTime) -> anyhow::Result<()>; - } + #[rstest] + fn test_new() { + let current_time = Utc::now(); + let state = Tracker::new(current_time, Duration::seconds(300)); + assert!(!state.is_idle); + assert!(!state.is_changed); } #[rstest] - #[tokio::test] - async fn test_mark_not_idle() { - let subscriber = Arc::new(MockSubscriber::new()); - let mut state = State::new(Duration::seconds(300), subscriber.clone()); + fn test_mark_not_idle() { + let current_time = Utc::now(); + let mut state = Tracker::new(current_time, Duration::seconds(300)); - state.mark_not_idle(); + state.mark_not_idle(current_time); assert!(!state.is_idle); assert!(state.is_changed); } #[rstest] - #[tokio::test] - async fn test_mark_idle() { - let subscriber = Arc::new(MockSubscriber::new()); - let mut state = State::new(Duration::seconds(300), subscriber.clone()); + fn test_mark_idle() { + let current_time = Utc::now(); + let mut state = Tracker::new(current_time, Duration::seconds(300)); - state.mark_idle(); + state.mark_idle(current_time); assert!(state.is_idle); assert!(state.is_changed); } + + #[rstest] + fn test_send_with_last_input() { + struct Time { + now: DateTime, + last_input_ago: u32, + } + + impl Time { + fn new() -> Self { + Self { + now: Utc::now(), + last_input_ago: 0, + } + } + + fn tick_inactive(&mut self) { + self.now += Duration::seconds(10); + self.last_input_ago += 10; + } + + fn tick_active(&mut self) { + self.now += Duration::seconds(10); + self.last_input_ago = 0; + } + } + + let mut time = Time::new(); + let mut tracker = Tracker::new(time.now, Duration::seconds(30)); + + time.tick_inactive(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Active { changed: false, .. })); + + time.tick_inactive(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Active { changed: false, .. })); + + time.tick_inactive(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Idle { changed: true, .. })); + + time.tick_inactive(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Idle { changed: false, .. })); + + time.tick_active(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Active { changed: true, .. })); + + time.tick_active(); + let status = tracker + .get_with_last_input(time.now, time.last_input_ago) + .unwrap(); + assert!(matches!(status, Status::Active { changed: false, .. })); + } + + struct TimeReactive { + now: DateTime, + } + + impl TimeReactive { + fn new() -> Self { + Self { + now: Utc.with_ymd_and_hms(2021, 3, 1, 13, 30, 0).unwrap(), + } + } + + fn tick(&mut self, seconds: i64) { + self.now += Duration::seconds(seconds); + } + + fn diff_seconds(&self, other: DateTime) -> i64 { + (self.now - other).num_seconds() + } + + fn assert_active_status( + &self, + status: &Status, + expected_changed: bool, + expected_last_input_seconds_ago: i64, + message: &str, + ) { + if let Status::Active { + changed, + last_input_time, + } = status + { + assert_eq!(expected_changed, *changed); + assert_eq!( + self.diff_seconds(*last_input_time), + expected_last_input_seconds_ago, + "{}", + message + ); + } else { + panic!("Expected active status"); + } + } + + fn assert_idle_status( + &self, + status: &Status, + expected_changed: bool, + last_input_ago: i64, + message: &str, + ) { + if let Status::Idle { + changed, + last_input_time, + duration, + } = status + { + assert_eq!(expected_changed, *changed); + assert_eq!( + self.diff_seconds(*last_input_time), + last_input_ago, + "{}", + message + ); + assert_eq!(duration.num_seconds(), last_input_ago, "{}", message); + } else { + panic!("Expected idle status"); + } + } + } + + #[rstest] + fn test_send_reactive() { + let mut time = TimeReactive::new(); + let mut tracker = Tracker::new(time.now, Duration::seconds(30)); + + // 15 seconds of active time + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + assert!(matches!(status, Status::Active { changed: false, .. })); + + time.tick(5); + // 30 seconds of idle time + tracker.mark_idle(time.now); + assert!(tracker.is_idle); + assert!(tracker.is_changed); + + time.tick(5); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status( + &status, + true, + 20, + "Marked idle 5s ago, last guaranteed activity is on creation as less than 30s interval", + ); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status(&status, false, 30, ""); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status(&status, false, 40, ""); + + time.tick(5); + tracker.mark_not_idle(time.now); + assert!(!tracker.is_idle); + assert!(tracker.is_changed); + + time.tick(5); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status( + &status, + true, + 5, + "Marked active 5s ago which is more recent than 30s interval ago.", + ); + assert!( + matches!(status, Status::Active { last_input_time, .. } if last_input_time >= time.now - Duration::seconds(5)) + ); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status(&status, false, 15, ""); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status( + &status, + false, + 25, + "Marked active 25s ago which is more recent than 30s interval ago.", + ); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status( + &status, + false, + 30, + "Marked active 35s ago, it will be active since 30s ago.", + ); + + time.tick(10); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status( + &status, + false, + 30, + "Marked active 45s ago, it will be active since 30s ago.", + ); + + time.tick(5); + tracker.mark_idle(time.now); + + time.tick(5); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status(&status, true, 40, "Last guaranteed activity 5+5+30s ago"); + + time.tick(30); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status(&status, false, 70, ""); + + // Short active time + time.tick(1); + tracker.mark_not_idle(time.now); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_active_status(&status, true, 0, ""); + assert!( + matches!(status, Status::Active { last_input_time, .. } if last_input_time == time.now) + ); + + time.tick(5); + tracker.mark_idle(time.now); + time.tick(5); + let status = tracker.get_reactive(time.now).unwrap(); + time.assert_idle_status(&status, true, 10, ""); + assert!( + matches!(status, Status::Idle { last_input_time, .. } if last_input_time == time.now - Duration::seconds(10)) + ); + } } diff --git a/watchers/src/watchers/wl_ext_idle_notify.rs b/watchers/src/watchers/wl_ext_idle_notify.rs index d0a35e3..7ebd05a 100644 --- a/watchers/src/watchers/wl_ext_idle_notify.rs +++ b/watchers/src/watchers/wl_ext_idle_notify.rs @@ -2,10 +2,9 @@ use super::idle; use super::wl_connection::{subscribe_state, WlEventConnection}; use super::Watcher; use crate::report_client::ReportClient; -use crate::subscriber::IdleSubscriber; use anyhow::anyhow; use async_trait::async_trait; -use chrono::TimeDelta; +use chrono::{TimeDelta, Utc}; use std::sync::Arc; use wayland_client::{ globals::GlobalListContents, @@ -18,7 +17,7 @@ use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtId struct WatcherState { idle_notification: ExtIdleNotificationV1, - idle_state: idle::State, + idle_state: idle::Tracker, } impl Drop for WatcherState { @@ -29,25 +28,19 @@ impl Drop for WatcherState { } impl WatcherState { - fn new( - idle_notification: ExtIdleNotificationV1, - idle_timeout: TimeDelta, - subscriber: Arc, - ) -> Self { + fn new(idle_notification: ExtIdleNotificationV1, idle_timeout: TimeDelta) -> Self { Self { idle_notification, - idle_state: idle::State::new(idle_timeout, subscriber), + idle_state: idle::Tracker::new(Utc::now(), idle_timeout), } } fn idle(&mut self) { - self.idle_state.mark_idle(); - debug!("Idle"); + self.idle_state.mark_idle(Utc::now()); } fn resume(&mut self) { - self.idle_state.mark_not_idle(); - debug!("Resumed"); + self.idle_state.mark_not_idle(Utc::now()); } } @@ -90,7 +83,6 @@ impl Watcher for IdleWatcher { .get_ext_idle_notification(timeout.unwrap()) .unwrap(), client.config.idle_timeout, - client.clone(), ); connection .event_queue @@ -103,12 +95,14 @@ impl Watcher for IdleWatcher { }) } - async fn run_iteration(&mut self, _: &Arc) -> anyhow::Result<()> { + async fn run_iteration(&mut self, client: &Arc) -> anyhow::Result<()> { self.connection .event_queue .roundtrip(&mut self.watcher_state) .map_err(|e| anyhow!("Event queue is not processed: {e}"))?; - self.watcher_state.idle_state.send_reactive().await + client + .handle_idle_status(self.watcher_state.idle_state.get_reactive(Utc::now())?) + .await } } diff --git a/watchers/src/watchers/wl_kwin_idle.rs b/watchers/src/watchers/wl_kwin_idle.rs index 7197ed2..6b6e332 100644 --- a/watchers/src/watchers/wl_kwin_idle.rs +++ b/watchers/src/watchers/wl_kwin_idle.rs @@ -2,10 +2,9 @@ use super::idle; use super::wl_connection::{subscribe_state, WlEventConnection}; use super::Watcher; use crate::report_client::ReportClient; -use crate::subscriber::IdleSubscriber; use anyhow::anyhow; use async_trait::async_trait; -use chrono::TimeDelta; +use chrono::{TimeDelta, Utc}; use std::sync::Arc; use wayland_client::{ globals::GlobalListContents, @@ -19,7 +18,7 @@ use wayland_protocols_plasma::idle::client::org_kde_kwin_idle_timeout::{ struct WatcherState { kwin_idle_timeout: OrgKdeKwinIdleTimeout, - idle_state: idle::State, + idle_state: idle::Tracker, } impl Drop for WatcherState { @@ -30,25 +29,21 @@ impl Drop for WatcherState { } impl WatcherState { - fn new( - kwin_idle_timeout: OrgKdeKwinIdleTimeout, - idle_timeout: TimeDelta, - subscriber: Arc, - ) -> Self { + fn new(kwin_idle_timeout: OrgKdeKwinIdleTimeout, idle_timeout: TimeDelta) -> Self { Self { kwin_idle_timeout, - idle_state: idle::State::new(idle_timeout, subscriber), + idle_state: idle::Tracker::new(Utc::now(), idle_timeout), } } fn idle(&mut self) { - self.idle_state.mark_idle(); - debug!("Idle"); + let time = Utc::now(); + self.idle_state.mark_idle(time); } fn resume(&mut self) { - self.idle_state.mark_not_idle(); - debug!("Resumed"); + let time = Utc::now(); + self.idle_state.mark_not_idle(time); } } @@ -89,7 +84,6 @@ impl Watcher for IdleWatcher { let mut watcher_state = WatcherState::new( connection.get_kwin_idle_timeout(timeout.unwrap()).unwrap(), client.config.idle_timeout, - client.clone(), ); connection .event_queue @@ -102,12 +96,14 @@ impl Watcher for IdleWatcher { }) } - async fn run_iteration(&mut self, _: &Arc) -> anyhow::Result<()> { + async fn run_iteration(&mut self, client: &Arc) -> anyhow::Result<()> { self.connection .event_queue .roundtrip(&mut self.watcher_state) .map_err(|e| anyhow!("Event queue is not processed: {e}"))?; - self.watcher_state.idle_state.send_reactive().await + client + .handle_idle_status(self.watcher_state.idle_state.get_reactive(Utc::now())?) + .await } } diff --git a/watchers/src/watchers/x11_screensaver_idle.rs b/watchers/src/watchers/x11_screensaver_idle.rs index ce6fae5..c6b0862 100644 --- a/watchers/src/watchers/x11_screensaver_idle.rs +++ b/watchers/src/watchers/x11_screensaver_idle.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use chrono::Utc; use super::{idle, x11_connection::X11Client, Watcher}; use crate::report_client::ReportClient; @@ -6,7 +7,7 @@ use std::sync::Arc; pub struct IdleWatcher { client: X11Client, - idle_state: idle::State, + idle_state: idle::Tracker, } impl IdleWatcher { @@ -25,14 +26,15 @@ impl Watcher for IdleWatcher { Ok(IdleWatcher { client, - idle_state: idle::State::new(report_client.config.idle_timeout, report_client.clone()), + idle_state: idle::Tracker::new(Utc::now(), report_client.config.idle_timeout), }) } - async fn run_iteration(&mut self, _: &Arc) -> anyhow::Result<()> { + async fn run_iteration(&mut self, client: &Arc) -> anyhow::Result<()> { let seconds = self.seconds_since_input().await?; - self.idle_state.send_with_last_input(seconds).await?; - Ok(()) + client + .handle_idle_status(self.idle_state.get_with_last_input(Utc::now(), seconds)?) + .await } }