mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-07 12:05:49 +00:00
Try to reconnect to X11 on error
X11 connection may sometimes be lost.
This commit is contained in:
parent
c8ca1debab
commit
f1f22089a9
11
README.md
11
README.md
@ -2,12 +2,13 @@
|
|||||||
[](https://github.com/2e3s/awatcher/actions?query=branch%3Amain) [](https://deps.rs/repo/github/2e3s/awatcher)
|
[](https://github.com/2e3s/awatcher/actions?query=branch%3Amain) [](https://deps.rs/repo/github/2e3s/awatcher)
|
||||||
|
|
||||||
Awatcher is a window activity and idle watcher with an optional tray and UI for statistics.
|
Awatcher is a window activity and idle watcher with an optional tray and UI for statistics.
|
||||||
The goal is to compensate the fragmentation of desktop environments on Linux,
|
The goal is to compensate the fragmentation of desktop environments on Linux by supporting all reportable environments,
|
||||||
and to add more flexibility to reports.
|
to add more flexibility to reports with filters, and to have better UX with the distribution by a single executable.
|
||||||
|
|
||||||
The server and web UI are taken from [ActivityWatch](https://github.com/ActivityWatch) project,
|
The foundation is taken from [ActivityWatch](https://github.com/ActivityWatch), which includes the server and web UI.
|
||||||
which has a worse support of Linux environment, with a pretty bulky distribution.
|
The unbundled watcher can replace the original idle and active window watchers in the original distribution if necessary.
|
||||||
The crate also provides a library with watchers which can send statistics to the server.
|
|
||||||
|
The crate also provides a library with watchers which can send the data to the server.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ pub struct IdleWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl idle::SinceLastInput for IdleWatcher {
|
impl idle::SinceLastInput for IdleWatcher {
|
||||||
fn seconds_since_input(&self) -> anyhow::Result<u32> {
|
fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
||||||
let ms = self
|
let ms = self
|
||||||
.dbus_connection
|
.dbus_connection
|
||||||
.call_method(
|
.call_method(
|
||||||
@ -26,10 +26,10 @@ impl idle::SinceLastInput for IdleWatcher {
|
|||||||
|
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
fn new() -> anyhow::Result<Self> {
|
fn new() -> anyhow::Result<Self> {
|
||||||
let watcher = Self {
|
let mut watcher = Self {
|
||||||
dbus_connection: Connection::session()?,
|
dbus_connection: Connection::session()?,
|
||||||
};
|
};
|
||||||
idle::SinceLastInput::seconds_since_input(&watcher)?;
|
idle::SinceLastInput::seconds_since_input(&mut watcher)?;
|
||||||
|
|
||||||
Ok(watcher)
|
Ok(watcher)
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,11 @@ use chrono::{Duration, Utc};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait SinceLastInput {
|
pub trait SinceLastInput {
|
||||||
fn seconds_since_input(&self) -> anyhow::Result<u32>;
|
fn seconds_since_input(&mut self) -> anyhow::Result<u32>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ping_since_last_input(
|
pub fn ping_since_last_input(
|
||||||
watcher: &impl SinceLastInput,
|
watcher: &mut impl SinceLastInput,
|
||||||
is_idle: bool,
|
is_idle: bool,
|
||||||
client: &Arc<ReportClient>,
|
client: &Arc<ReportClient>,
|
||||||
) -> anyhow::Result<bool> {
|
) -> anyhow::Result<bool> {
|
||||||
|
@ -11,12 +11,12 @@ pub struct WindowData {
|
|||||||
pub app_id: String,
|
pub app_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct X11Connection {
|
pub struct X11Client {
|
||||||
connection: RustConnection,
|
connection: RustConnection,
|
||||||
screen_root: Window,
|
screen_root: Window,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl X11Connection {
|
impl X11Client {
|
||||||
pub fn new() -> anyhow::Result<Self> {
|
pub fn new() -> anyhow::Result<Self> {
|
||||||
if env::var("DISPLAY").is_err() {
|
if env::var("DISPLAY").is_err() {
|
||||||
warn!("DISPLAY is not set, setting to the default value \":0\"");
|
warn!("DISPLAY is not set, setting to the default value \":0\"");
|
||||||
@ -26,32 +26,58 @@ impl X11Connection {
|
|||||||
let (connection, screen_num) = x11rb::connect(None)?;
|
let (connection, screen_num) = x11rb::connect(None)?;
|
||||||
let screen_root = connection.setup().roots[screen_num].root;
|
let screen_root = connection.setup().roots[screen_num].root;
|
||||||
|
|
||||||
Ok(X11Connection {
|
Ok(X11Client {
|
||||||
connection,
|
connection,
|
||||||
screen_root,
|
screen_root,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seconds_since_last_input(&self) -> anyhow::Result<u32> {
|
fn reconnect(&mut self) {
|
||||||
let reply = self
|
match x11rb::connect(None) {
|
||||||
|
Ok((connection, screen_num)) => {
|
||||||
|
self.screen_root = connection.setup().roots[screen_num].root;
|
||||||
|
self.connection = connection;
|
||||||
|
}
|
||||||
|
Err(e) => error!("Failed to reconnect to X11: {e}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_with_reconnect<T>(
|
||||||
|
&mut self,
|
||||||
|
action: fn(&Self) -> anyhow::Result<T>,
|
||||||
|
) -> anyhow::Result<T> {
|
||||||
|
match action(self) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(_) => {
|
||||||
|
self.reconnect();
|
||||||
|
action(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seconds_since_last_input(&mut self) -> anyhow::Result<u32> {
|
||||||
|
self.execute_with_reconnect(|client| {
|
||||||
|
let reply = client
|
||||||
.connection
|
.connection
|
||||||
.screensaver_query_info(self.screen_root)?
|
.screensaver_query_info(client.screen_root)?
|
||||||
.reply()?;
|
.reply()?;
|
||||||
|
|
||||||
Ok(reply.ms_since_user_input / 1000)
|
Ok(reply.ms_since_user_input / 1000)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn active_window_data(&self) -> anyhow::Result<WindowData> {
|
pub fn active_window_data(&mut self) -> anyhow::Result<WindowData> {
|
||||||
let focus: Window = self.find_active_window()?;
|
self.execute_with_reconnect(|client| {
|
||||||
|
let focus: Window = client.find_active_window()?;
|
||||||
|
|
||||||
let name = self.get_property(
|
let name = client.get_property(
|
||||||
focus,
|
focus,
|
||||||
self.intern_atom("_NET_WM_NAME")?,
|
client.intern_atom("_NET_WM_NAME")?,
|
||||||
"_NET_WM_NAME",
|
"_NET_WM_NAME",
|
||||||
self.intern_atom("UTF8_STRING")?,
|
client.intern_atom("UTF8_STRING")?,
|
||||||
u32::MAX,
|
u32::MAX,
|
||||||
)?;
|
)?;
|
||||||
let class = self.get_property(
|
let class = client.get_property(
|
||||||
focus,
|
focus,
|
||||||
AtomEnum::WM_CLASS.into(),
|
AtomEnum::WM_CLASS.into(),
|
||||||
"WM_CLASS",
|
"WM_CLASS",
|
||||||
@ -65,6 +91,7 @@ impl X11Connection {
|
|||||||
title: title.to_string(),
|
title: title.to_string(),
|
||||||
app_id: parse_wm_class(&class)?.to_string(),
|
app_id: parse_wm_class(&class)?.to_string(),
|
||||||
})
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_property(
|
fn get_property(
|
||||||
|
@ -1,25 +1,25 @@
|
|||||||
use super::{idle, x11_connection::X11Connection, Watcher};
|
use super::{idle, x11_connection::X11Client, Watcher};
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use std::{sync::Arc, thread};
|
use std::{sync::Arc, thread};
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
connection: X11Connection,
|
client: X11Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl idle::SinceLastInput for IdleWatcher {
|
impl idle::SinceLastInput for IdleWatcher {
|
||||||
fn seconds_since_input(&self) -> anyhow::Result<u32> {
|
fn seconds_since_input(&mut self) -> anyhow::Result<u32> {
|
||||||
self.connection.seconds_since_last_input()
|
self.client.seconds_since_last_input()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Watcher for IdleWatcher {
|
impl Watcher for IdleWatcher {
|
||||||
fn new() -> anyhow::Result<Self> {
|
fn new() -> anyhow::Result<Self> {
|
||||||
let connection = X11Connection::new()?;
|
let mut client = X11Client::new()?;
|
||||||
|
|
||||||
// Check if screensaver extension is supported
|
// Check if screensaver extension is supported
|
||||||
connection.seconds_since_last_input()?;
|
client.seconds_since_last_input()?;
|
||||||
|
|
||||||
Ok(IdleWatcher { connection })
|
Ok(IdleWatcher { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>) {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use super::{x11_connection::X11Connection, Watcher};
|
use super::{x11_connection::X11Client, Watcher};
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
pub struct WindowWatcher {
|
pub struct WindowWatcher {
|
||||||
connection: X11Connection,
|
client: X11Client,
|
||||||
last_title: String,
|
last_title: String,
|
||||||
last_app_id: String,
|
last_app_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowWatcher {
|
impl WindowWatcher {
|
||||||
fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
||||||
let data = self.connection.active_window_data()?;
|
let data = self.client.active_window_data()?;
|
||||||
|
|
||||||
if data.app_id != self.last_app_id || data.title != self.last_title {
|
if data.app_id != self.last_app_id || data.title != self.last_title {
|
||||||
debug!(
|
debug!(
|
||||||
@ -30,11 +30,11 @@ impl WindowWatcher {
|
|||||||
|
|
||||||
impl Watcher for WindowWatcher {
|
impl Watcher for WindowWatcher {
|
||||||
fn new() -> anyhow::Result<Self> {
|
fn new() -> anyhow::Result<Self> {
|
||||||
let connection = X11Connection::new()?;
|
let mut client = X11Client::new()?;
|
||||||
connection.active_window_data()?;
|
client.active_window_data()?;
|
||||||
|
|
||||||
Ok(WindowWatcher {
|
Ok(WindowWatcher {
|
||||||
connection,
|
client,
|
||||||
last_title: String::new(),
|
last_title: String::new(),
|
||||||
last_app_id: String::new(),
|
last_app_id: String::new(),
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user