mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-04 02:20:15 +00:00
Extract tracker service and fix reactive timings
This commit is contained in:
parent
1b41533e1e
commit
ddfe972cff
72
Cargo.lock
generated
72
Cargo.lock
generated
@ -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",
|
||||
|
@ -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" }
|
||||
|
@ -3,7 +3,6 @@ extern crate log;
|
||||
|
||||
pub mod config;
|
||||
mod report_client;
|
||||
mod subscriber;
|
||||
mod watchers;
|
||||
|
||||
pub use crate::report_client::ReportClient;
|
||||
|
@ -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 {}",
|
||||
|
@ -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<Utc>,
|
||||
duration: TimeDelta,
|
||||
) -> anyhow::Result<()>;
|
||||
|
||||
async fn non_idle(&self, changed: bool, last_input_time: DateTime<Utc>) -> anyhow::Result<()>;
|
||||
}
|
@ -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;
|
||||
|
@ -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<ReportClient>) -> anyhow::Result<()> {
|
||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> 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
|
||||
}
|
||||
}
|
||||
|
@ -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<Utc>,
|
||||
changed_time: DateTime<Utc>,
|
||||
is_idle: bool,
|
||||
is_changed: bool,
|
||||
idle_timeout: TimeDelta,
|
||||
subscriber: Arc<dyn IdleSubscriber>,
|
||||
|
||||
idle_start: Option<DateTime<Utc>>,
|
||||
idle_end: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(idle_timeout: TimeDelta, subscriber: Arc<dyn IdleSubscriber>) -> Self {
|
||||
pub enum Status {
|
||||
Idle {
|
||||
changed: bool,
|
||||
last_input_time: DateTime<Utc>,
|
||||
duration: TimeDelta,
|
||||
},
|
||||
Active {
|
||||
changed: bool,
|
||||
last_input_time: DateTime<Utc>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Tracker {
|
||||
pub fn new(now: DateTime<Utc>, 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<Utc>) {
|
||||
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<Utc>) {
|
||||
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<Utc>) {
|
||||
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<Utc>,
|
||||
seconds_since_input: u32,
|
||||
) -> anyhow::Result<Status> {
|
||||
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<Utc>) -> anyhow::Result<Status> {
|
||||
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.");
|
||||
self.last_input_time = max(self.last_input_time, now - self.idle_timeout);
|
||||
|
||||
// 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(());
|
||||
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<Utc>) -> anyhow::Result<()> {
|
||||
if self.is_changed {
|
||||
fn get_status(&mut self, now: DateTime<Utc>) -> 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<Utc>, duration: TimeDelta) -> anyhow::Result<()>;
|
||||
async fn non_idle(&self, changed: bool, last_input_time: DateTime<Utc>) -> 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<Utc>,
|
||||
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<Utc>,
|
||||
}
|
||||
|
||||
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<Utc>) -> 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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn IdleSubscriber>,
|
||||
) -> 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<ReportClient>) -> anyhow::Result<()> {
|
||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> 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
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn IdleSubscriber>,
|
||||
) -> 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<ReportClient>) -> anyhow::Result<()> {
|
||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> 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
|
||||
}
|
||||
}
|
||||
|
@ -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<ReportClient>) -> anyhow::Result<()> {
|
||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> 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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user