mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Unify idle logic
This commit is contained in:
parent
f960dd6697
commit
ea215b432d
@ -1,16 +1,17 @@
|
|||||||
use super::{gnome_wayland::load_watcher, gnome_wayland::GnomeWatcher, idle, Watcher};
|
use super::{gnome_wayland::load_watcher, idle, Watcher};
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use chrono::Duration;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
dbus_connection: Connection,
|
dbus_connection: Connection,
|
||||||
is_idle: bool,
|
idle_state: idle::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl idle::SinceLastInput for IdleWatcher {
|
impl IdleWatcher {
|
||||||
async fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
async fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
||||||
let ms = self
|
let ms = self
|
||||||
.dbus_connection
|
.dbus_connection
|
||||||
@ -27,26 +28,26 @@ impl idle::SinceLastInput for IdleWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GnomeWatcher for IdleWatcher {
|
|
||||||
async fn load() -> anyhow::Result<Self> {
|
|
||||||
let mut watcher = Self {
|
|
||||||
dbus_connection: Connection::session().await?,
|
|
||||||
is_idle: false,
|
|
||||||
};
|
|
||||||
idle::SinceLastInput::seconds_since_input(&mut watcher).await?;
|
|
||||||
|
|
||||||
Ok(watcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
async fn new(_: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
load_watcher().await
|
let duration = Duration::from_std(client.config.idle_timeout).unwrap();
|
||||||
|
load_watcher(|| async move {
|
||||||
|
let mut watcher = Self {
|
||||||
|
dbus_connection: Connection::session().await?,
|
||||||
|
idle_state: idle::State::new(duration),
|
||||||
|
};
|
||||||
|
watcher.seconds_since_input().await?;
|
||||||
|
Ok(watcher)
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
self.is_idle = idle::ping_since_last_input(self, self.is_idle, client).await?;
|
let seconds = self.seconds_since_input().await?;
|
||||||
|
self.idle_state
|
||||||
|
.send_with_last_input(seconds, client)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
// The extension may not be loaded and available right away in Gnome, this mod will retry a few times.
|
use std::future::Future;
|
||||||
pub trait GnomeWatcher {
|
|
||||||
async fn load() -> anyhow::Result<Self>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_gnome() -> bool {
|
fn is_gnome() -> bool {
|
||||||
if let Ok(de) = std::env::var("XDG_CURRENT_DESKTOP") {
|
if let Ok(de) = std::env::var("XDG_CURRENT_DESKTOP") {
|
||||||
@ -21,12 +16,16 @@ fn is_wayland() -> bool {
|
|||||||
.contains("wayland")
|
.contains("wayland")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn load_watcher<T: GnomeWatcher>() -> anyhow::Result<T> {
|
pub async fn load_watcher<T, F, Fut>(loader: F) -> anyhow::Result<T>
|
||||||
|
where
|
||||||
|
F: Fn() -> Fut,
|
||||||
|
Fut: Future<Output = anyhow::Result<T>>,
|
||||||
|
{
|
||||||
if is_gnome() && is_wayland() {
|
if is_gnome() && is_wayland() {
|
||||||
debug!("Gnome Wayland detected");
|
debug!("Gnome Wayland detected");
|
||||||
let mut watcher = Err(anyhow::anyhow!(""));
|
let mut watcher = Err(anyhow::anyhow!(""));
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
watcher = T::load().await;
|
watcher = loader().await;
|
||||||
if let Err(e) = &watcher {
|
if let Err(e) = &watcher {
|
||||||
debug!("Failed to load Gnome Wayland watcher: {e}");
|
debug!("Failed to load Gnome Wayland watcher: {e}");
|
||||||
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
|
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
|
||||||
@ -35,6 +34,6 @@ pub async fn load_watcher<T: GnomeWatcher>() -> anyhow::Result<T> {
|
|||||||
|
|
||||||
watcher
|
watcher
|
||||||
} else {
|
} else {
|
||||||
T::load().await
|
loader().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use serde::Deserialize;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
use super::{gnome_wayland::load_watcher, gnome_wayland::GnomeWatcher, Watcher};
|
use super::{gnome_wayland::load_watcher, Watcher};
|
||||||
|
|
||||||
pub struct WindowWatcher {
|
pub struct WindowWatcher {
|
||||||
dbus_connection: Connection,
|
dbus_connection: Connection,
|
||||||
@ -79,23 +79,20 @@ impl WindowWatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GnomeWatcher for WindowWatcher {
|
|
||||||
async fn load() -> anyhow::Result<Self> {
|
|
||||||
let watcher = Self {
|
|
||||||
dbus_connection: Connection::session().await?,
|
|
||||||
last_app_id: String::new(),
|
|
||||||
last_title: String::new(),
|
|
||||||
};
|
|
||||||
watcher.get_window_data().await?;
|
|
||||||
|
|
||||||
Ok(watcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Watcher for WindowWatcher {
|
impl Watcher for WindowWatcher {
|
||||||
async fn new(_: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
async fn new(_: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
load_watcher().await
|
load_watcher(|| async move {
|
||||||
|
let watcher = Self {
|
||||||
|
dbus_connection: Connection::session().await?,
|
||||||
|
last_app_id: String::new(),
|
||||||
|
last_title: String::new(),
|
||||||
|
};
|
||||||
|
watcher.get_window_data().await?;
|
||||||
|
|
||||||
|
Ok(watcher)
|
||||||
|
})
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
|
@ -1,53 +1,119 @@
|
|||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait SinceLastInput {
|
pub struct State {
|
||||||
async fn seconds_since_input(&mut self) -> anyhow::Result<u32>;
|
last_input_time: DateTime<Utc>,
|
||||||
|
is_idle: bool,
|
||||||
|
is_changed: bool,
|
||||||
|
idle_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn ping_since_last_input(
|
impl State {
|
||||||
watcher: &mut impl SinceLastInput,
|
pub fn new(idle_timeout: Duration) -> Self {
|
||||||
is_idle: bool,
|
Self {
|
||||||
client: &Arc<ReportClient>,
|
last_input_time: Utc::now(),
|
||||||
) -> anyhow::Result<bool> {
|
is_idle: false,
|
||||||
// The logic is rewritten from the original Python code:
|
is_changed: false,
|
||||||
// https://github.com/ActivityWatch/aw-watcher-afk/blob/ef531605cd8238e00138bbb980e5457054e05248/aw_watcher_afk/afk.py#L73
|
idle_timeout,
|
||||||
let duration_1ms: Duration = Duration::milliseconds(1);
|
|
||||||
let duration_zero: Duration = Duration::zero();
|
|
||||||
|
|
||||||
let seconds_since_input = watcher.seconds_since_input().await?;
|
|
||||||
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).await?;
|
|
||||||
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)
|
|
||||||
.await?;
|
|
||||||
} 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).await?;
|
|
||||||
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)
|
|
||||||
.await?;
|
|
||||||
} 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).await?;
|
|
||||||
} else {
|
|
||||||
trace!("Reporting as not idle");
|
|
||||||
client.ping(is_idle, last_input, duration_zero).await?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(is_idle_again)
|
pub fn mark_not_idle(&mut self) {
|
||||||
|
self.is_idle = false;
|
||||||
|
self.is_changed = true;
|
||||||
|
self.last_input_time = Utc::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_idle(&mut self) {
|
||||||
|
self.is_idle = true;
|
||||||
|
self.is_changed = true;
|
||||||
|
self.last_input_time -= self.idle_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
client: &Arc<ReportClient>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let time_since_input = Duration::seconds(i64::from(seconds_since_input));
|
||||||
|
|
||||||
|
self.last_input_time = Utc::now() - time_since_input;
|
||||||
|
|
||||||
|
if self.is_idle
|
||||||
|
&& u64::from(seconds_since_input) < self.idle_timeout.num_seconds().try_into().unwrap()
|
||||||
|
{
|
||||||
|
debug!("No longer idle");
|
||||||
|
self.is_idle = false;
|
||||||
|
self.is_changed = true;
|
||||||
|
} else if !self.is_idle
|
||||||
|
&& u64::from(seconds_since_input) >= self.idle_timeout.num_seconds().try_into().unwrap()
|
||||||
|
{
|
||||||
|
debug!("Idle again");
|
||||||
|
self.is_idle = true;
|
||||||
|
self.is_changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_ping(client).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn send_reactive(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
|
let now = Utc::now();
|
||||||
|
if !self.is_idle {
|
||||||
|
self.last_input_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.send_ping(client).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_ping(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
|
let now = Utc::now();
|
||||||
|
|
||||||
|
if self.is_changed {
|
||||||
|
let result = if self.is_idle {
|
||||||
|
debug!("Reporting as changed to idle");
|
||||||
|
client
|
||||||
|
.ping(false, self.last_input_time, Duration::zero())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// ping with timestamp+1ms with the next event (to ensure the latest event gets retrieved by get_event)
|
||||||
|
client
|
||||||
|
.ping(
|
||||||
|
true,
|
||||||
|
self.last_input_time + Duration::milliseconds(1),
|
||||||
|
Duration::zero(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
debug!("Reporting as no longer idle");
|
||||||
|
|
||||||
|
client
|
||||||
|
.ping(true, self.last_input_time, Duration::zero())
|
||||||
|
.await?;
|
||||||
|
client
|
||||||
|
.ping(
|
||||||
|
false,
|
||||||
|
self.last_input_time + Duration::milliseconds(1),
|
||||||
|
Duration::zero(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
};
|
||||||
|
self.is_changed = false;
|
||||||
|
result
|
||||||
|
} else if self.is_idle {
|
||||||
|
trace!("Reporting as idle");
|
||||||
|
client
|
||||||
|
.ping(true, self.last_input_time, now - self.last_input_time)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
trace!("Reporting as not idle");
|
||||||
|
client
|
||||||
|
.ping(false, self.last_input_time, Duration::zero())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
use super::idle;
|
||||||
use super::wl_connection::{subscribe_state, WlEventConnection};
|
use super::wl_connection::{subscribe_state, WlEventConnection};
|
||||||
use super::Watcher;
|
use super::Watcher;
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::Duration;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
globals::GlobalListContents,
|
globals::GlobalListContents,
|
||||||
@ -14,101 +15,43 @@ use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::E
|
|||||||
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::ExtIdleNotificationV1;
|
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::ExtIdleNotificationV1;
|
||||||
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
|
use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
|
||||||
|
|
||||||
struct IdleState {
|
struct WatcherState {
|
||||||
idle_notification: ExtIdleNotificationV1,
|
idle_notification: ExtIdleNotificationV1,
|
||||||
last_input_time: DateTime<Utc>,
|
idle_state: idle::State,
|
||||||
is_idle: bool,
|
|
||||||
is_changed: bool,
|
|
||||||
idle_timeout: Duration,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for IdleState {
|
impl Drop for WatcherState {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!("Releasing idle notification");
|
info!("Releasing idle notification");
|
||||||
self.idle_notification.destroy();
|
self.idle_notification.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdleState {
|
impl WatcherState {
|
||||||
fn new(idle_notification: ExtIdleNotificationV1, idle_timeout: Duration) -> Self {
|
fn new(idle_notification: ExtIdleNotificationV1, idle_timeout: Duration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
idle_notification,
|
idle_notification,
|
||||||
last_input_time: Utc::now(),
|
idle_state: idle::State::new(idle_timeout),
|
||||||
is_idle: false,
|
|
||||||
is_changed: false,
|
|
||||||
idle_timeout,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idle(&mut self) {
|
fn idle(&mut self) {
|
||||||
self.is_idle = true;
|
self.idle_state.mark_idle();
|
||||||
self.is_changed = true;
|
|
||||||
self.last_input_time -= self.idle_timeout;
|
|
||||||
debug!("Idle");
|
debug!("Idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&mut self) {
|
fn resume(&mut self) {
|
||||||
self.is_idle = false;
|
self.idle_state.mark_not_idle();
|
||||||
self.last_input_time = Utc::now();
|
|
||||||
self.is_changed = true;
|
|
||||||
debug!("Resumed");
|
debug!("Resumed");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_ping(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
|
||||||
let now = Utc::now();
|
|
||||||
if !self.is_idle {
|
|
||||||
self.last_input_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_changed {
|
|
||||||
let result = if self.is_idle {
|
|
||||||
debug!("Reporting as changed to idle");
|
|
||||||
client
|
|
||||||
.ping(false, self.last_input_time, Duration::zero())
|
|
||||||
.await?;
|
|
||||||
client
|
|
||||||
.ping(
|
|
||||||
true,
|
|
||||||
self.last_input_time + Duration::milliseconds(1),
|
|
||||||
Duration::zero(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
debug!("Reporting as no longer idle");
|
|
||||||
|
|
||||||
client
|
|
||||||
.ping(true, self.last_input_time, Duration::zero())
|
|
||||||
.await?;
|
|
||||||
client
|
|
||||||
.ping(
|
|
||||||
false,
|
|
||||||
self.last_input_time + Duration::milliseconds(1),
|
|
||||||
Duration::zero(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
self.is_changed = false;
|
|
||||||
result
|
|
||||||
} else if self.is_idle {
|
|
||||||
trace!("Reporting as idle");
|
|
||||||
client
|
|
||||||
.ping(true, self.last_input_time, now - self.last_input_time)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
trace!("Reporting as not idle");
|
|
||||||
client
|
|
||||||
.ping(false, self.last_input_time, Duration::zero())
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe_state!(wl_registry::WlRegistry, GlobalListContents, IdleState);
|
subscribe_state!(wl_registry::WlRegistry, GlobalListContents, WatcherState);
|
||||||
subscribe_state!(wl_registry::WlRegistry, (), IdleState);
|
subscribe_state!(wl_registry::WlRegistry, (), WatcherState);
|
||||||
subscribe_state!(WlSeat, (), IdleState);
|
subscribe_state!(WlSeat, (), WatcherState);
|
||||||
subscribe_state!(ExtIdleNotifierV1, (), IdleState);
|
subscribe_state!(ExtIdleNotifierV1, (), WatcherState);
|
||||||
|
|
||||||
impl Dispatch<ExtIdleNotificationV1, ()> for IdleState {
|
impl Dispatch<ExtIdleNotificationV1, ()> for WatcherState {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
_: &ExtIdleNotificationV1,
|
_: &ExtIdleNotificationV1,
|
||||||
@ -126,37 +69,40 @@ impl Dispatch<ExtIdleNotificationV1, ()> for IdleState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
connection: WlEventConnection<IdleState>,
|
connection: WlEventConnection<WatcherState>,
|
||||||
idle_state: IdleState,
|
watcher_state: WatcherState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
let mut connection: WlEventConnection<IdleState> = WlEventConnection::connect()?;
|
let mut connection: WlEventConnection<WatcherState> = WlEventConnection::connect()?;
|
||||||
connection.get_ext_idle()?;
|
connection.get_ext_idle()?;
|
||||||
|
|
||||||
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
||||||
let mut idle_state = IdleState::new(
|
let mut watcher_state = WatcherState::new(
|
||||||
connection
|
connection
|
||||||
.get_ext_idle_notification(timeout.unwrap())
|
.get_ext_idle_notification(timeout.unwrap())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
Duration::from_std(client.config.idle_timeout).unwrap(),
|
Duration::from_std(client.config.idle_timeout).unwrap(),
|
||||||
);
|
);
|
||||||
connection.event_queue.roundtrip(&mut idle_state).unwrap();
|
connection
|
||||||
|
.event_queue
|
||||||
|
.roundtrip(&mut watcher_state)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
connection,
|
connection,
|
||||||
idle_state,
|
watcher_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
self.connection
|
self.connection
|
||||||
.event_queue
|
.event_queue
|
||||||
.roundtrip(&mut self.idle_state)
|
.roundtrip(&mut self.watcher_state)
|
||||||
.map_err(|e| anyhow!("Event queue is not processed: {e}"))?;
|
.map_err(|e| anyhow!("Event queue is not processed: {e}"))?;
|
||||||
|
|
||||||
self.idle_state.send_ping(client).await
|
self.watcher_state.idle_state.send_reactive(client).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
use super::idle;
|
||||||
use super::wl_connection::{subscribe_state, WlEventConnection};
|
use super::wl_connection::{subscribe_state, WlEventConnection};
|
||||||
use super::Watcher;
|
use super::Watcher;
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::Duration;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
globals::GlobalListContents,
|
globals::GlobalListContents,
|
||||||
@ -15,101 +16,43 @@ use wayland_protocols_plasma::idle::client::org_kde_kwin_idle_timeout::{
|
|||||||
Event as OrgKdeKwinIdleTimeoutEvent, OrgKdeKwinIdleTimeout,
|
Event as OrgKdeKwinIdleTimeoutEvent, OrgKdeKwinIdleTimeout,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IdleState {
|
struct WatcherState {
|
||||||
kwin_idle_timeout: OrgKdeKwinIdleTimeout,
|
kwin_idle_timeout: OrgKdeKwinIdleTimeout,
|
||||||
last_input_time: DateTime<Utc>,
|
idle_state: idle::State,
|
||||||
is_idle: bool,
|
|
||||||
is_changed: bool,
|
|
||||||
idle_timeout: Duration,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for IdleState {
|
impl Drop for WatcherState {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
info!("Releasing idle timeout");
|
info!("Releasing idle timeout");
|
||||||
self.kwin_idle_timeout.release();
|
self.kwin_idle_timeout.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdleState {
|
impl WatcherState {
|
||||||
fn new(kwin_idle_timeout: OrgKdeKwinIdleTimeout, idle_timeout: Duration) -> Self {
|
fn new(kwin_idle_timeout: OrgKdeKwinIdleTimeout, idle_timeout: Duration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kwin_idle_timeout,
|
kwin_idle_timeout,
|
||||||
last_input_time: Utc::now(),
|
idle_state: idle::State::new(idle_timeout),
|
||||||
is_idle: false,
|
|
||||||
is_changed: false,
|
|
||||||
idle_timeout,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn idle(&mut self) {
|
fn idle(&mut self) {
|
||||||
self.is_idle = true;
|
self.idle_state.mark_idle();
|
||||||
self.is_changed = true;
|
|
||||||
self.last_input_time -= self.idle_timeout;
|
|
||||||
debug!("Idle");
|
debug!("Idle");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resume(&mut self) {
|
fn resume(&mut self) {
|
||||||
self.is_idle = false;
|
self.idle_state.mark_not_idle();
|
||||||
self.last_input_time = Utc::now();
|
|
||||||
self.is_changed = true;
|
|
||||||
debug!("Resumed");
|
debug!("Resumed");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_ping(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
|
||||||
let now = Utc::now();
|
|
||||||
if !self.is_idle {
|
|
||||||
self.last_input_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.is_changed {
|
|
||||||
let result = if self.is_idle {
|
|
||||||
debug!("Reporting as changed to idle");
|
|
||||||
client
|
|
||||||
.ping(false, self.last_input_time, Duration::zero())
|
|
||||||
.await?;
|
|
||||||
client
|
|
||||||
.ping(
|
|
||||||
true,
|
|
||||||
self.last_input_time + Duration::milliseconds(1),
|
|
||||||
Duration::zero(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
debug!("Reporting as no longer idle");
|
|
||||||
|
|
||||||
client
|
|
||||||
.ping(true, self.last_input_time, Duration::zero())
|
|
||||||
.await?;
|
|
||||||
client
|
|
||||||
.ping(
|
|
||||||
false,
|
|
||||||
self.last_input_time + Duration::milliseconds(1),
|
|
||||||
Duration::zero(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
};
|
|
||||||
self.is_changed = false;
|
|
||||||
result
|
|
||||||
} else if self.is_idle {
|
|
||||||
trace!("Reporting as idle");
|
|
||||||
client
|
|
||||||
.ping(true, self.last_input_time, now - self.last_input_time)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
trace!("Reporting as not idle");
|
|
||||||
client
|
|
||||||
.ping(false, self.last_input_time, Duration::zero())
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe_state!(wl_registry::WlRegistry, GlobalListContents, IdleState);
|
subscribe_state!(wl_registry::WlRegistry, GlobalListContents, WatcherState);
|
||||||
subscribe_state!(wl_registry::WlRegistry, (), IdleState);
|
subscribe_state!(wl_registry::WlRegistry, (), WatcherState);
|
||||||
subscribe_state!(WlSeat, (), IdleState);
|
subscribe_state!(WlSeat, (), WatcherState);
|
||||||
subscribe_state!(OrgKdeKwinIdle, (), IdleState);
|
subscribe_state!(OrgKdeKwinIdle, (), WatcherState);
|
||||||
|
|
||||||
impl Dispatch<OrgKdeKwinIdleTimeout, ()> for IdleState {
|
impl Dispatch<OrgKdeKwinIdleTimeout, ()> for WatcherState {
|
||||||
fn event(
|
fn event(
|
||||||
state: &mut Self,
|
state: &mut Self,
|
||||||
_: &OrgKdeKwinIdleTimeout,
|
_: &OrgKdeKwinIdleTimeout,
|
||||||
@ -127,35 +70,38 @@ impl Dispatch<OrgKdeKwinIdleTimeout, ()> for IdleState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
connection: WlEventConnection<IdleState>,
|
connection: WlEventConnection<WatcherState>,
|
||||||
idle_state: IdleState,
|
watcher_state: WatcherState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
async fn new(client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
let mut connection: WlEventConnection<IdleState> = WlEventConnection::connect()?;
|
let mut connection: WlEventConnection<WatcherState> = WlEventConnection::connect()?;
|
||||||
connection.get_kwin_idle()?;
|
connection.get_kwin_idle()?;
|
||||||
|
|
||||||
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
||||||
let mut idle_state = IdleState::new(
|
let mut watcher_state = WatcherState::new(
|
||||||
connection.get_kwin_idle_timeout(timeout.unwrap()).unwrap(),
|
connection.get_kwin_idle_timeout(timeout.unwrap()).unwrap(),
|
||||||
Duration::from_std(client.config.idle_timeout).unwrap(),
|
Duration::from_std(client.config.idle_timeout).unwrap(),
|
||||||
);
|
);
|
||||||
connection.event_queue.roundtrip(&mut idle_state).unwrap();
|
connection
|
||||||
|
.event_queue
|
||||||
|
.roundtrip(&mut watcher_state)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
connection,
|
connection,
|
||||||
idle_state,
|
watcher_state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
self.connection
|
self.connection
|
||||||
.event_queue
|
.event_queue
|
||||||
.roundtrip(&mut self.idle_state)
|
.roundtrip(&mut self.watcher_state)
|
||||||
.map_err(|e| anyhow!("Event queue is not processed: {e}"))?;
|
.map_err(|e| anyhow!("Event queue is not processed: {e}"))?;
|
||||||
|
|
||||||
self.idle_state.send_ping(client).await
|
self.watcher_state.idle_state.send_reactive(client).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
use super::{idle, x11_connection::X11Client, Watcher};
|
use super::{idle, x11_connection::X11Client, Watcher};
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
@ -6,10 +7,10 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
client: X11Client,
|
client: X11Client,
|
||||||
is_idle: bool,
|
idle_state: idle::State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl idle::SinceLastInput for IdleWatcher {
|
impl IdleWatcher {
|
||||||
async fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
async fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
||||||
self.client.seconds_since_last_input()
|
self.client.seconds_since_last_input()
|
||||||
}
|
}
|
||||||
@ -17,7 +18,7 @@ impl idle::SinceLastInput for IdleWatcher {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
async fn new(_: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
async fn new(report_client: &Arc<ReportClient>) -> anyhow::Result<Self> {
|
||||||
let mut client = X11Client::new()?;
|
let mut client = X11Client::new()?;
|
||||||
|
|
||||||
// Check if screensaver extension is supported
|
// Check if screensaver extension is supported
|
||||||
@ -25,12 +26,17 @@ impl Watcher for IdleWatcher {
|
|||||||
|
|
||||||
Ok(IdleWatcher {
|
Ok(IdleWatcher {
|
||||||
client,
|
client,
|
||||||
is_idle: false,
|
idle_state: idle::State::new(
|
||||||
|
Duration::from_std(report_client.config.idle_timeout).unwrap(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
async fn run_iteration(&mut self, client: &Arc<ReportClient>) -> anyhow::Result<()> {
|
||||||
self.is_idle = idle::ping_since_last_input(self, self.is_idle, client).await?;
|
let seconds = self.seconds_since_input().await?;
|
||||||
|
self.idle_state
|
||||||
|
.send_with_last_input(seconds, client)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user