mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-07 12:05:49 +00:00
Implement window watcher for Gnome
This commit is contained in:
parent
3c51ad8685
commit
a841b69e47
@ -16,7 +16,7 @@ pub struct Config {
|
|||||||
pub poll_time_window: Duration,
|
pub poll_time_window: Duration,
|
||||||
pub idle_bucket_name: String,
|
pub idle_bucket_name: String,
|
||||||
pub active_window_bucket_name: String,
|
pub active_window_bucket_name: String,
|
||||||
pub mock_server: bool,
|
pub no_server: bool,
|
||||||
filters: Vec<Filter>,
|
filters: Vec<Filter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ impl Config {
|
|||||||
arg!(--"poll-time-window" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
arg!(--"poll-time-window" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
||||||
.value_parser(value_parser!(u32))
|
.value_parser(value_parser!(u32))
|
||||||
.default_value(defaults::poll_time_window_seconds().to_string()),
|
.default_value(defaults::poll_time_window_seconds().to_string()),
|
||||||
arg!(--"mock-server" "Don't communicate to the ActivityWatch server")
|
arg!(--"no-server" "Don't communicate to the ActivityWatch server")
|
||||||
.value_parser(value_parser!(bool))
|
.value_parser(value_parser!(bool))
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
])
|
])
|
||||||
@ -63,7 +63,7 @@ impl Config {
|
|||||||
idle_bucket_name,
|
idle_bucket_name,
|
||||||
active_window_bucket_name,
|
active_window_bucket_name,
|
||||||
filters: config.client.filters,
|
filters: config.client.filters,
|
||||||
mock_server: *matches.get_one("mock-server").unwrap(),
|
no_server: *matches.get_one("no-server").unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ fn main() -> anyhow::Result<()> {
|
|||||||
let client = ReportClient::new(Config::from_cli()?)?;
|
let client = ReportClient::new(Config::from_cli()?)?;
|
||||||
let client = Arc::new(client);
|
let client = Arc::new(client);
|
||||||
|
|
||||||
if client.config.mock_server {
|
if client.config.no_server {
|
||||||
warn!(
|
warn!(
|
||||||
"Not sending to server {}:{}",
|
"Not sending to server {}:{}",
|
||||||
client.config.host, client.config.port
|
client.config.host, client.config.port
|
||||||
|
@ -13,7 +13,7 @@ impl ReportClient {
|
|||||||
pub fn new(config: Config) -> anyhow::Result<Self> {
|
pub fn new(config: Config) -> anyhow::Result<Self> {
|
||||||
let client = AwClient::new(&config.host, &config.port.to_string(), "awatcher");
|
let client = AwClient::new(&config.host, &config.port.to_string(), "awatcher");
|
||||||
|
|
||||||
if !config.mock_server {
|
if !config.no_server {
|
||||||
Self::create_bucket(&client, &config.idle_bucket_name, "afkstatus")?;
|
Self::create_bucket(&client, &config.idle_bucket_name, "afkstatus")?;
|
||||||
Self::create_bucket(&client, &config.active_window_bucket_name, "currentwindow")?;
|
Self::create_bucket(&client, &config.active_window_bucket_name, "currentwindow")?;
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ impl ReportClient {
|
|||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.config.mock_server {
|
if self.config.no_server {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ impl ReportClient {
|
|||||||
data,
|
data,
|
||||||
};
|
};
|
||||||
|
|
||||||
if self.config.mock_server {
|
if self.config.no_server {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod gnome_idle;
|
mod gnome_idle;
|
||||||
|
mod gnome_window;
|
||||||
mod idle;
|
mod idle;
|
||||||
mod kwin_window;
|
mod kwin_window;
|
||||||
mod wl_bindings;
|
mod wl_bindings;
|
||||||
@ -56,6 +57,7 @@ pub const IDLE: &WatcherConstructors = &[
|
|||||||
|
|
||||||
pub const ACTIVE_WINDOW: &WatcherConstructors = &[
|
pub const ACTIVE_WINDOW: &WatcherConstructors = &[
|
||||||
watcher!(wl_foreign_toplevel::WindowWatcher),
|
watcher!(wl_foreign_toplevel::WindowWatcher),
|
||||||
watcher!(kwin_window::WindowWatcher),
|
|
||||||
watcher!(x11_window::WindowWatcher),
|
watcher!(x11_window::WindowWatcher),
|
||||||
|
watcher!(kwin_window::WindowWatcher),
|
||||||
|
watcher!(gnome_window::WindowWatcher),
|
||||||
];
|
];
|
||||||
|
88
src/watchers/gnome_window.rs
Normal file
88
src/watchers/gnome_window.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use crate::report_client::ReportClient;
|
||||||
|
use anyhow::Context;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use std::{sync::Arc, thread};
|
||||||
|
use zbus::blocking::Connection;
|
||||||
|
|
||||||
|
use super::Watcher;
|
||||||
|
|
||||||
|
pub struct WindowWatcher {
|
||||||
|
dbus_connection: Connection,
|
||||||
|
last_title: String,
|
||||||
|
last_app_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Default)]
|
||||||
|
struct WindowData {
|
||||||
|
title: String,
|
||||||
|
wm_class: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowWatcher {
|
||||||
|
fn get_window_data(&self) -> anyhow::Result<WindowData> {
|
||||||
|
let call_response = self.dbus_connection.call_method(
|
||||||
|
Some("org.gnome.Shell"),
|
||||||
|
"/org/gnome/shell/extensions/FocusedWindow",
|
||||||
|
Some("org.gnome.shell.extensions.FocusedWindow"),
|
||||||
|
"Get",
|
||||||
|
&(),
|
||||||
|
);
|
||||||
|
|
||||||
|
match call_response {
|
||||||
|
Ok(json) => {
|
||||||
|
let json = json
|
||||||
|
.body::<String>()
|
||||||
|
.with_context(|| "DBus interface cannot be parsed as string")?;
|
||||||
|
serde_json::from_str(&json).with_context(|| {
|
||||||
|
"DBus interface org.gnome.shell.extensions.FocusedWindow returned wrong JSON"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if e.to_string().contains("No window in focus") {
|
||||||
|
Ok(WindowData::default())
|
||||||
|
} else {
|
||||||
|
Err(e.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
||||||
|
let data = self.get_window_data()?;
|
||||||
|
|
||||||
|
if data.wm_class != self.last_app_id || data.title != self.last_title {
|
||||||
|
debug!(
|
||||||
|
r#"Changed window app_id="{}", title="{}""#,
|
||||||
|
data.wm_class, data.title
|
||||||
|
);
|
||||||
|
self.last_app_id = data.wm_class;
|
||||||
|
self.last_title = data.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
client
|
||||||
|
.send_active_window(&self.last_app_id, &self.last_title)
|
||||||
|
.with_context(|| "Failed to send heartbeat for active window")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Watcher for WindowWatcher {
|
||||||
|
fn new() -> anyhow::Result<Self> {
|
||||||
|
let watcher = Self {
|
||||||
|
dbus_connection: Connection::session()?,
|
||||||
|
last_app_id: String::new(),
|
||||||
|
last_title: String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(watcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn watch(&mut self, client: &Arc<ReportClient>) {
|
||||||
|
info!("Starting active window watcher");
|
||||||
|
loop {
|
||||||
|
if let Err(error) = self.send_active_window(client) {
|
||||||
|
error!("Error on active window: {error}");
|
||||||
|
}
|
||||||
|
thread::sleep(client.config.poll_time_window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ impl WindowWatcher {
|
|||||||
|
|
||||||
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!(
|
||||||
"Changed window app_id=\"{}\", title=\"{}\"",
|
r#"Changed window app_id="{}", title="{}""#,
|
||||||
data.app_id, data.title
|
data.app_id, data.title
|
||||||
);
|
);
|
||||||
self.last_app_id = data.app_id;
|
self.last_app_id = data.app_id;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user