mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Introduce anyhow errors
This commit is contained in:
parent
11cfb7c54b
commit
3c51ad8685
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -60,6 +60,12 @@ dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.5.1"
|
||||
@ -216,6 +222,7 @@ dependencies = [
|
||||
name = "awatcher"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aw-client-rust",
|
||||
"chrono",
|
||||
"clap",
|
||||
|
@ -21,3 +21,4 @@ dirs = "5.0.0"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_default = "0.1.0"
|
||||
regex = "1.8.1"
|
||||
anyhow = "1.0.70"
|
||||
|
@ -2,7 +2,6 @@ mod defaults;
|
||||
mod file_config;
|
||||
mod filters;
|
||||
|
||||
use crate::BoxedError;
|
||||
use clap::{arg, value_parser, ArgAction, Command};
|
||||
use file_config::FileConfig;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
@ -22,7 +21,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_cli() -> Result<Self, BoxedError> {
|
||||
pub fn from_cli() -> anyhow::Result<Self> {
|
||||
let matches = Command::new("Activity Watcher")
|
||||
.version("0.1.0")
|
||||
.about("A set of ActivityWatch desktop watchers")
|
||||
|
@ -1,3 +1,4 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use clap::{parser::ValueSource, ArgMatches};
|
||||
use serde::Deserialize;
|
||||
use serde_default::DefaultFromSerde;
|
||||
@ -7,7 +8,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::{config::defaults, BoxedError};
|
||||
use crate::config::defaults;
|
||||
|
||||
use super::filters::Filter;
|
||||
|
||||
@ -56,8 +57,9 @@ pub struct FileConfig {
|
||||
}
|
||||
|
||||
impl FileConfig {
|
||||
pub fn new(matches: &ArgMatches) -> Result<Self, BoxedError> {
|
||||
let mut config_path: PathBuf = dirs::config_dir().ok_or("Config directory is unknown")?;
|
||||
pub fn new(matches: &ArgMatches) -> anyhow::Result<Self> {
|
||||
let mut config_path: PathBuf =
|
||||
dirs::config_dir().ok_or(anyhow!("Config directory is unknown"))?;
|
||||
config_path.push("awatcher");
|
||||
config_path.push("config.toml");
|
||||
if matches.contains_id("config") {
|
||||
@ -73,8 +75,9 @@ impl FileConfig {
|
||||
|
||||
let mut config = if config_path.exists() {
|
||||
debug!("Reading config at {}", config_path.display());
|
||||
let config_content = std::fs::read_to_string(config_path)
|
||||
.map_err(|e| format!("Impossible to read config file: {e}"))?;
|
||||
let config_content = std::fs::read_to_string(&config_path).with_context(|| {
|
||||
format!("Impossible to read config file {}", config_path.display())
|
||||
})?;
|
||||
|
||||
toml::from_str(&config_content)?
|
||||
} else {
|
||||
|
@ -11,12 +11,10 @@ use config::Config;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use report_client::ReportClient;
|
||||
use std::env;
|
||||
use std::{error::Error, str::FromStr, sync::Arc, thread};
|
||||
use std::{sync::Arc, thread};
|
||||
|
||||
use crate::watchers::ConstructorFilter;
|
||||
|
||||
type BoxedError = Box<dyn Error>;
|
||||
|
||||
fn setup_logger() -> Result<(), fern::InitError> {
|
||||
let log_setting = env::var("AWATCHER_LOG").unwrap_or("info".to_string());
|
||||
|
||||
@ -37,14 +35,14 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
||||
.level(log::LevelFilter::Warn)
|
||||
.level_for(
|
||||
"awatcher",
|
||||
FromStr::from_str(&log_setting).unwrap_or(log::LevelFilter::Info),
|
||||
log_setting.parse().unwrap_or(log::LevelFilter::Info),
|
||||
)
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), BoxedError> {
|
||||
fn main() -> anyhow::Result<()> {
|
||||
setup_logger()?;
|
||||
|
||||
let client = ReportClient::new(Config::from_cli()?)?;
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::error::Error;
|
||||
|
||||
use super::BoxedError;
|
||||
use super::Config;
|
||||
use anyhow::Context;
|
||||
use aw_client_rust::{AwClient, Event as AwEvent};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use serde_json::{Map, Value};
|
||||
@ -12,7 +10,7 @@ pub struct ReportClient {
|
||||
}
|
||||
|
||||
impl ReportClient {
|
||||
pub fn new(config: Config) -> Result<Self, BoxedError> {
|
||||
pub fn new(config: Config) -> anyhow::Result<Self> {
|
||||
let client = AwClient::new(&config.host, &config.port.to_string(), "awatcher");
|
||||
|
||||
if !config.mock_server {
|
||||
@ -28,7 +26,7 @@ impl ReportClient {
|
||||
is_idle: bool,
|
||||
timestamp: DateTime<Utc>,
|
||||
duration: Duration,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
) -> anyhow::Result<()> {
|
||||
let mut data = Map::new();
|
||||
data.insert(
|
||||
"status".to_string(),
|
||||
@ -49,10 +47,10 @@ impl ReportClient {
|
||||
let pulsetime = (self.config.idle_timeout + self.config.poll_time_idle).as_secs_f64();
|
||||
self.client
|
||||
.heartbeat(&self.config.idle_bucket_name, &event, pulsetime)
|
||||
.map_err(|_| "Failed to send heartbeat".into())
|
||||
.with_context(|| "Failed to send heartbeat")
|
||||
}
|
||||
|
||||
pub fn send_active_window(&self, app_id: &str, title: &str) -> Result<(), BoxedError> {
|
||||
pub fn send_active_window(&self, app_id: &str, title: &str) -> anyhow::Result<()> {
|
||||
let mut data = Map::new();
|
||||
let mut data_insert = |k: &str, v: String| data.insert(k.to_string(), Value::String(v));
|
||||
|
||||
@ -89,16 +87,16 @@ impl ReportClient {
|
||||
&event,
|
||||
interval_margin,
|
||||
)
|
||||
.map_err(|_| "Failed to send heartbeat for active window".into())
|
||||
.with_context(|| "Failed to send heartbeat for active window")
|
||||
}
|
||||
|
||||
fn create_bucket(
|
||||
client: &AwClient,
|
||||
bucket_name: &str,
|
||||
bucket_type: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
) -> anyhow::Result<()> {
|
||||
client
|
||||
.create_bucket_simple(bucket_name, bucket_type)
|
||||
.map_err(|e| format!("Failed to create bucket {bucket_name}: {e}").into())
|
||||
.with_context(|| format!("Failed to create bucket {bucket_name}"))
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ mod x11_connection;
|
||||
mod x11_screensaver_idle;
|
||||
mod x11_window;
|
||||
|
||||
use crate::{report_client::ReportClient, BoxedError};
|
||||
use crate::report_client::ReportClient;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait Watcher: Send {
|
||||
fn new() -> Result<Self, BoxedError>
|
||||
fn new() -> anyhow::Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
fn watch(&mut self, client: &Arc<ReportClient>);
|
||||
@ -21,7 +21,7 @@ pub trait Watcher: Send {
|
||||
|
||||
type BoxedWatcher = Box<dyn Watcher>;
|
||||
|
||||
type WatcherConstructor = (&'static str, fn() -> Result<BoxedWatcher, BoxedError>);
|
||||
type WatcherConstructor = (&'static str, fn() -> anyhow::Result<BoxedWatcher>);
|
||||
type WatcherConstructors = [WatcherConstructor];
|
||||
|
||||
pub trait ConstructorFilter {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{idle, Watcher};
|
||||
use crate::{report_client::ReportClient, BoxedError};
|
||||
use crate::report_client::ReportClient;
|
||||
use anyhow::Context;
|
||||
use std::{sync::Arc, thread};
|
||||
use zbus::blocking::Connection;
|
||||
|
||||
@ -8,7 +9,7 @@ pub struct IdleWatcher {
|
||||
}
|
||||
|
||||
impl idle::SinceLastInput for IdleWatcher {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError> {
|
||||
fn seconds_since_input(&self) -> anyhow::Result<u32> {
|
||||
let ms = self
|
||||
.dbus_connection
|
||||
.call_method(
|
||||
@ -19,12 +20,12 @@ impl idle::SinceLastInput for IdleWatcher {
|
||||
&(),
|
||||
)?
|
||||
.body::<u64>()?;
|
||||
u32::try_from(ms / 1000).map_err(|_| format!("Number {ms} is invalid").into())
|
||||
u32::try_from(ms / 1000).with_context(|| format!("Number {ms} is invalid"))
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn new() -> Result<Self, crate::BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let watcher = Self {
|
||||
dbus_connection: Connection::session()?,
|
||||
};
|
||||
|
@ -1,16 +1,16 @@
|
||||
use crate::{report_client::ReportClient, BoxedError};
|
||||
use crate::report_client::ReportClient;
|
||||
use chrono::{Duration, Utc};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait SinceLastInput {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError>;
|
||||
fn seconds_since_input(&self) -> anyhow::Result<u32>;
|
||||
}
|
||||
|
||||
pub fn ping_since_last_input(
|
||||
watcher: &impl SinceLastInput,
|
||||
is_idle: bool,
|
||||
client: &Arc<ReportClient>,
|
||||
) -> Result<bool, BoxedError> {
|
||||
) -> anyhow::Result<bool> {
|
||||
// The logic is rewritten from the original Python code:
|
||||
// https://github.com/ActivityWatch/aw-watcher-afk/blob/ef531605cd8238e00138bbb980e5457054e05248/aw_watcher_afk/afk.py#L73
|
||||
let duration_1ms: Duration = Duration::milliseconds(1);
|
||||
|
@ -3,8 +3,9 @@
|
||||
* For the moment of writing, KWin doesn't implement the appropriate protocols to get a top level window.
|
||||
* Inspired by https://github.com/k0kubun/xremap/
|
||||
*/
|
||||
use super::{BoxedError, Watcher};
|
||||
use super::Watcher;
|
||||
use crate::report_client::ReportClient;
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::env::temp_dir;
|
||||
use std::path::Path;
|
||||
use std::sync::{mpsc::channel, Arc, Mutex};
|
||||
@ -28,7 +29,7 @@ impl KWinScript {
|
||||
}
|
||||
}
|
||||
|
||||
fn load(&mut self) -> Result<(), BoxedError> {
|
||||
fn load(&mut self) -> anyhow::Result<()> {
|
||||
let path = temp_dir().join("kwin_window.js");
|
||||
std::fs::write(&path, KWIN_SCRIPT).unwrap();
|
||||
|
||||
@ -41,7 +42,7 @@ impl KWinScript {
|
||||
result
|
||||
}
|
||||
|
||||
fn is_loaded(&self) -> Result<bool, BoxedError> {
|
||||
fn is_loaded(&self) -> anyhow::Result<bool> {
|
||||
self.dbus_connection
|
||||
.call_method(
|
||||
Some("org.kde.KWin"),
|
||||
@ -54,10 +55,10 @@ impl KWinScript {
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
fn get_registered_number(&self, path: &Path) -> Result<i32, BoxedError> {
|
||||
fn get_registered_number(&self, path: &Path) -> anyhow::Result<i32> {
|
||||
let temp_path = path
|
||||
.to_str()
|
||||
.ok_or::<BoxedError>("Temporary file path is not valid".into())?;
|
||||
.ok_or(anyhow!("Temporary file path is not valid"))?;
|
||||
|
||||
self.dbus_connection
|
||||
.call_method(
|
||||
@ -72,7 +73,7 @@ impl KWinScript {
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
fn unload(&self) -> Result<bool, BoxedError> {
|
||||
fn unload(&self) -> anyhow::Result<bool> {
|
||||
self.dbus_connection
|
||||
.call_method(
|
||||
Some("org.kde.KWin"),
|
||||
@ -85,7 +86,7 @@ impl KWinScript {
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
fn start(&self, script_number: i32) -> Result<(), BoxedError> {
|
||||
fn start(&self, script_number: i32) -> anyhow::Result<()> {
|
||||
debug!("Starting KWin script {script_number}");
|
||||
self.dbus_connection
|
||||
.call_method(
|
||||
@ -95,8 +96,8 @@ impl KWinScript {
|
||||
"run",
|
||||
&(),
|
||||
)
|
||||
.map_err(|e| format!("Error on starting the script {e}").into())
|
||||
.map(|_| ())
|
||||
.with_context(|| "Error on starting the script")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,12 +115,12 @@ impl Drop for KWinScript {
|
||||
fn send_active_window(
|
||||
client: &ReportClient,
|
||||
active_window: &Arc<Mutex<ActiveWindow>>,
|
||||
) -> Result<(), BoxedError> {
|
||||
let active_window = active_window.lock().map_err(|e| format!("{e}"))?;
|
||||
) -> anyhow::Result<()> {
|
||||
let active_window = active_window.lock().expect("Lock cannot be acquired");
|
||||
|
||||
client
|
||||
.send_active_window(&active_window.resource_class, &active_window.caption)
|
||||
.map_err(|_| "Failed to send heartbeat for active window".into())
|
||||
.with_context(|| "Failed to send heartbeat for active window")
|
||||
}
|
||||
|
||||
struct ActiveWindow {
|
||||
@ -153,7 +154,7 @@ pub struct WindowWatcher {
|
||||
}
|
||||
|
||||
impl Watcher for WindowWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let kwin_script = KWinScript::new(Connection::session()?);
|
||||
if kwin_script.is_loaded()? {
|
||||
warn!("KWin script is already loaded, unloading");
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::wl_bindings;
|
||||
use crate::BoxedError;
|
||||
use anyhow::Context;
|
||||
use wayland_client::{
|
||||
globals::{registry_queue_init, GlobalList, GlobalListContents},
|
||||
protocol::{wl_registry, wl_seat::WlSeat},
|
||||
@ -38,9 +38,9 @@ where
|
||||
+ Dispatch<wl_registry::WlRegistry, ()>
|
||||
+ 'static,
|
||||
{
|
||||
pub fn connect() -> Result<Self, BoxedError> {
|
||||
let connection =
|
||||
Connection::connect_to_env().map_err(|_| "Unable to connect to Wayland compositor")?;
|
||||
pub fn connect() -> anyhow::Result<Self> {
|
||||
let connection = Connection::connect_to_env()
|
||||
.with_context(|| "Unable to connect to Wayland compositor")?;
|
||||
let display = connection.display();
|
||||
let (globals, event_queue) = registry_queue_init::<T>(&connection)?;
|
||||
|
||||
@ -55,7 +55,7 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_foreign_toplevel_manager(&self) -> Result<ZwlrForeignToplevelManagerV1, BoxedError>
|
||||
pub fn get_foreign_toplevel_manager(&self) -> anyhow::Result<ZwlrForeignToplevelManagerV1>
|
||||
where
|
||||
T: Dispatch<ZwlrForeignToplevelManagerV1, ()>,
|
||||
{
|
||||
@ -68,7 +68,7 @@ where
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
pub fn get_kwin_idle(&self) -> Result<OrgKdeKwinIdle, BoxedError>
|
||||
pub fn get_kwin_idle(&self) -> anyhow::Result<OrgKdeKwinIdle>
|
||||
where
|
||||
T: Dispatch<OrgKdeKwinIdle, ()>,
|
||||
{
|
||||
@ -81,7 +81,7 @@ where
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
pub fn get_kwin_idle_timeout(&self, timeout: u32) -> Result<OrgKdeKwinIdleTimeout, BoxedError>
|
||||
pub fn get_kwin_idle_timeout(&self, timeout: u32) -> anyhow::Result<OrgKdeKwinIdleTimeout>
|
||||
where
|
||||
T: Dispatch<OrgKdeKwinIdle, ()>
|
||||
+ Dispatch<OrgKdeKwinIdleTimeout, ()>
|
||||
|
@ -5,9 +5,9 @@ use super::wl_bindings::wlr_foreign_toplevel::zwlr_foreign_toplevel_manager_v1::
|
||||
Event as ManagerEvent, ZwlrForeignToplevelManagerV1, EVT_TOPLEVEL_OPCODE,
|
||||
};
|
||||
use super::wl_connection::WlEventConnection;
|
||||
use super::BoxedError;
|
||||
use super::{wl_connection::subscribe_state, Watcher};
|
||||
use crate::report_client::ReportClient;
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::collections::HashMap;
|
||||
use std::{sync::Arc, thread};
|
||||
use wayland_client::{
|
||||
@ -114,18 +114,18 @@ impl Dispatch<ZwlrForeignToplevelHandleV1, ()> for ToplevelState {
|
||||
}
|
||||
|
||||
impl ToplevelState {
|
||||
fn send_active_window(&self) -> Result<(), BoxedError> {
|
||||
fn send_active_window(&self) -> anyhow::Result<()> {
|
||||
let active_window_id = self
|
||||
.current_window_id
|
||||
.as_ref()
|
||||
.ok_or("Current window is unknown")?;
|
||||
let active_window = self.windows.get(active_window_id).ok_or(format!(
|
||||
.ok_or(anyhow!("Current window is unknown"))?;
|
||||
let active_window = self.windows.get(active_window_id).ok_or(anyhow!(
|
||||
"Current window is not found by ID {active_window_id}"
|
||||
))?;
|
||||
|
||||
self.client
|
||||
.send_active_window(&active_window.app_id, &active_window.title)
|
||||
.map_err(|_| "Failed to send heartbeat for active window".into())
|
||||
.with_context(|| "Failed to send heartbeat for active window")
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ pub struct WindowWatcher {
|
||||
}
|
||||
|
||||
impl Watcher for WindowWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let connection: WlEventConnection<ToplevelState> = WlEventConnection::connect()?;
|
||||
connection.get_foreign_toplevel_manager()?;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use super::wl_bindings;
|
||||
use super::wl_connection::{subscribe_state, WlEventConnection};
|
||||
use super::BoxedError;
|
||||
use super::Watcher;
|
||||
use crate::report_client::ReportClient;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
@ -54,7 +53,7 @@ impl IdleState {
|
||||
debug!("Resumed");
|
||||
}
|
||||
|
||||
fn send_ping(&mut self) -> Result<(), BoxedError> {
|
||||
fn send_ping(&mut self) -> anyhow::Result<()> {
|
||||
let now = Utc::now();
|
||||
if !self.is_idle {
|
||||
self.last_input_time = now;
|
||||
@ -122,7 +121,7 @@ pub struct IdleWatcher {
|
||||
}
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let connection: WlEventConnection<IdleState> = WlEventConnection::connect()?;
|
||||
connection.get_kwin_idle()?;
|
||||
|
||||
|
@ -1,13 +1,11 @@
|
||||
use std::{env, str};
|
||||
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use log::warn;
|
||||
use std::{env, str};
|
||||
use x11rb::connection::Connection;
|
||||
use x11rb::protocol::screensaver::ConnectionExt as ScreensaverConnectionExt;
|
||||
use x11rb::protocol::xproto::{Atom, AtomEnum, ConnectionExt, GetPropertyReply, Window};
|
||||
use x11rb::rust_connection::RustConnection;
|
||||
|
||||
use crate::BoxedError;
|
||||
|
||||
pub struct WindowData {
|
||||
pub title: String,
|
||||
pub app_id: String,
|
||||
@ -19,7 +17,7 @@ pub struct X11Connection {
|
||||
}
|
||||
|
||||
impl X11Connection {
|
||||
pub fn new() -> Result<Self, BoxedError> {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
if env::var("DISPLAY").is_err() {
|
||||
warn!("DISPLAY is not set, setting to the default value \":0\"");
|
||||
env::set_var("DISPLAY", ":0");
|
||||
@ -34,7 +32,7 @@ impl X11Connection {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn seconds_since_last_input(&self) -> Result<u32, BoxedError> {
|
||||
pub fn seconds_since_last_input(&self) -> anyhow::Result<u32> {
|
||||
let reply = self
|
||||
.connection
|
||||
.screensaver_query_info(self.screen_root)?
|
||||
@ -43,7 +41,7 @@ impl X11Connection {
|
||||
Ok(reply.ms_since_user_input / 1000)
|
||||
}
|
||||
|
||||
pub fn active_window_data(&self) -> Result<WindowData, BoxedError> {
|
||||
pub fn active_window_data(&self) -> anyhow::Result<WindowData> {
|
||||
let focus: Window = self.find_active_window()?;
|
||||
|
||||
let name = self.get_property(
|
||||
@ -61,7 +59,7 @@ impl X11Connection {
|
||||
u32::MAX,
|
||||
)?;
|
||||
|
||||
let title = str::from_utf8(&name.value).map_err(|e| format!("Invalid title UTF: {e}"))?;
|
||||
let title = str::from_utf8(&name.value).with_context(|| "Invalid title UTF")?;
|
||||
|
||||
Ok(WindowData {
|
||||
title: title.to_string(),
|
||||
@ -76,25 +74,25 @@ impl X11Connection {
|
||||
property_name: &str,
|
||||
property_type: Atom,
|
||||
long_length: u32,
|
||||
) -> Result<GetPropertyReply, BoxedError> {
|
||||
) -> anyhow::Result<GetPropertyReply> {
|
||||
self.connection
|
||||
.get_property(false, window, property, property_type, 0, long_length)
|
||||
.map_err(|e| format!("GetPropertyRequest[{property_name}] failed: {e}"))?
|
||||
.with_context(|| format!("GetPropertyRequest[{property_name}] failed"))?
|
||||
.reply()
|
||||
.map_err(|e| format!("GetPropertyReply[{property_name}] failed: {e}").into())
|
||||
.with_context(|| format!("GetPropertyReply[{property_name}] failed"))
|
||||
}
|
||||
|
||||
fn intern_atom(&self, name: &str) -> Result<Atom, BoxedError> {
|
||||
fn intern_atom(&self, name: &str) -> anyhow::Result<Atom> {
|
||||
Ok(self
|
||||
.connection
|
||||
.intern_atom(false, name.as_bytes())
|
||||
.map_err(|_| format!("InternAtomRequest[{name}] failed"))?
|
||||
.with_context(|| format!("InternAtomRequest[{name}] failed"))?
|
||||
.reply()
|
||||
.map_err(|_| format!("InternAtomReply[{name}] failed"))?
|
||||
.with_context(|| format!("InternAtomReply[{name}] failed"))?
|
||||
.atom)
|
||||
}
|
||||
|
||||
fn find_active_window(&self) -> Result<Window, BoxedError> {
|
||||
fn find_active_window(&self) -> anyhow::Result<Window> {
|
||||
let window: Atom = AtomEnum::WINDOW.into();
|
||||
let net_active_window = self.intern_atom("_NET_ACTIVE_WINDOW")?;
|
||||
let active_window = self.get_property(
|
||||
@ -108,25 +106,25 @@ impl X11Connection {
|
||||
if active_window.format == 32 && active_window.length == 1 {
|
||||
active_window
|
||||
.value32()
|
||||
.ok_or("Invalid message. Expected value with format = 32")?
|
||||
.ok_or(anyhow!("Invalid message. Expected value with format = 32"))?
|
||||
.next()
|
||||
.ok_or("Active window is not found".into())
|
||||
.ok_or(anyhow!("Active window is not found"))
|
||||
} else {
|
||||
// Query the input focus
|
||||
Ok(self
|
||||
.connection
|
||||
.get_input_focus()
|
||||
.map_err(|e| format!("Failed to get input focus: {e}"))?
|
||||
.with_context(|| "Failed to get input focus")?
|
||||
.reply()
|
||||
.map_err(|e| format!("Failed to read input focus from reply: {e}"))?
|
||||
.with_context(|| "Failed to read input focus from reply")?
|
||||
.focus)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_wm_class(property: &GetPropertyReply) -> Result<&str, BoxedError> {
|
||||
fn parse_wm_class(property: &GetPropertyReply) -> anyhow::Result<&str> {
|
||||
if property.format != 8 {
|
||||
return Err("Malformed property: wrong format".into());
|
||||
bail!("Malformed property: wrong format");
|
||||
}
|
||||
let value = &property.value;
|
||||
// The property should contain two null-terminated strings. Find them.
|
||||
@ -140,6 +138,6 @@ fn parse_wm_class(property: &GetPropertyReply) -> Result<&str, BoxedError> {
|
||||
}
|
||||
Ok(std::str::from_utf8(class)?)
|
||||
} else {
|
||||
Err("Missing null byte".into())
|
||||
bail!("Missing null byte")
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,19 @@
|
||||
use std::{sync::Arc, thread};
|
||||
|
||||
use super::{idle, x11_connection::X11Connection, BoxedError, Watcher};
|
||||
use super::{idle, x11_connection::X11Connection, Watcher};
|
||||
use crate::report_client::ReportClient;
|
||||
use std::{sync::Arc, thread};
|
||||
|
||||
pub struct IdleWatcher {
|
||||
connection: X11Connection,
|
||||
}
|
||||
|
||||
impl idle::SinceLastInput for IdleWatcher {
|
||||
fn seconds_since_input(&self) -> Result<u32, BoxedError> {
|
||||
fn seconds_since_input(&self) -> anyhow::Result<u32> {
|
||||
self.connection.seconds_since_last_input()
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher for IdleWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let connection = X11Connection::new()?;
|
||||
|
||||
// Check if screensaver extension is supported
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{x11_connection::X11Connection, BoxedError, Watcher};
|
||||
use super::{x11_connection::X11Connection, Watcher};
|
||||
use crate::report_client::ReportClient;
|
||||
use anyhow::Context;
|
||||
use std::thread;
|
||||
|
||||
pub struct WindowWatcher {
|
||||
@ -9,7 +10,7 @@ pub struct WindowWatcher {
|
||||
}
|
||||
|
||||
impl WindowWatcher {
|
||||
fn send_active_window(&mut self, client: &ReportClient) -> Result<(), BoxedError> {
|
||||
fn send_active_window(&mut self, client: &ReportClient) -> anyhow::Result<()> {
|
||||
let data = self.connection.active_window_data()?;
|
||||
|
||||
if data.app_id != self.last_app_id || data.title != self.last_title {
|
||||
@ -23,12 +24,12 @@ impl WindowWatcher {
|
||||
|
||||
client
|
||||
.send_active_window(&self.last_app_id, &self.last_title)
|
||||
.map_err(|_| "Failed to send heartbeat for active window".into())
|
||||
.with_context(|| "Failed to send heartbeat for active window")
|
||||
}
|
||||
}
|
||||
|
||||
impl Watcher for WindowWatcher {
|
||||
fn new() -> Result<Self, crate::BoxedError> {
|
||||
fn new() -> anyhow::Result<Self> {
|
||||
let connection = X11Connection::new()?;
|
||||
connection.active_window_data()?;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user