mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-06 11:35:46 +00:00
Extract watchers into module
This commit is contained in:
parent
1740ec00c6
commit
63bfe4181e
30
.github/workflows/verify.yml
vendored
30
.github/workflows/verify.yml
vendored
@ -12,33 +12,17 @@ jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
override: true
|
||||
default: true
|
||||
- name: cargo fmt
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: -- --check
|
||||
- run: cargo fmt --check --workspace --all
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: clippy
|
||||
profile: minimal
|
||||
default: true
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run cargo check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
- run: cargo clippy --locked --all-targets --all-features --workspace -- -D warnings
|
||||
|
39
Cargo.lock
generated
39
Cargo.lock
generated
@ -62,9 +62,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
|
||||
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
@ -220,26 +220,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "awatcher"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aw-client-rust",
|
||||
"chrono",
|
||||
"clap",
|
||||
"dirs 5.0.0",
|
||||
"fern",
|
||||
"gethostname 0.4.1",
|
||||
"log",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_default",
|
||||
"serde_json",
|
||||
"toml",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
"x11rb",
|
||||
"zbus",
|
||||
"watchers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2062,6 +2052,27 @@ version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "watchers"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aw-client-rust",
|
||||
"chrono",
|
||||
"dirs 5.0.0",
|
||||
"log",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_default",
|
||||
"serde_json",
|
||||
"toml",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-scanner",
|
||||
"x11rb",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-backend"
|
||||
version = "0.1.1"
|
||||
|
32
Cargo.toml
32
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "awatcher"
|
||||
version = "0.1.0"
|
||||
version = "0.0.1"
|
||||
authors = ["Demmie <2e3s19@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
@ -8,32 +8,24 @@ edition = "2021"
|
||||
name = "awatcher"
|
||||
path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
name = "awatcher"
|
||||
crate-type = ["lib"]
|
||||
path = "src/lib.rs"
|
||||
[workspace]
|
||||
members = ["watchers"]
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.70"
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
|
||||
[dependencies]
|
||||
aw-client-rust = { git = "https://github.com/ActivityWatch/aw-server-rust" }
|
||||
watchers = { path = "./watchers", default-features = false }
|
||||
gethostname = "0.4.1"
|
||||
wayland-client = "0.30.1"
|
||||
wayland-scanner = "0.30"
|
||||
wayland-backend = "0.1"
|
||||
x11rb = { version = "0.11.1", features = ["screensaver"] }
|
||||
zbus = {version = "3.11.1", optional = true}
|
||||
chrono = "0.4.24"
|
||||
toml = "0.7.3"
|
||||
clap = { version = "4.2.1", features = ["string"] }
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
fern = { version = "0.6.2", features = ["colored"] }
|
||||
dirs = "5.0.0"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_default = "0.1.0"
|
||||
serde_json = "1.0.95"
|
||||
regex = "1.8.1"
|
||||
anyhow = "1.0.70"
|
||||
log = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["gnome", "kwin_window"]
|
||||
gnome = ["zbus"]
|
||||
kwin_window = ["zbus"]
|
||||
gnome = ["watchers/gnome"]
|
||||
kwin_window = ["watchers/kwin_window"]
|
||||
|
204
src/config.rs
204
src/config.rs
@ -1,92 +1,140 @@
|
||||
mod defaults;
|
||||
mod file_config;
|
||||
mod filters;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use self::filters::{Filter, Replacement};
|
||||
use clap::{arg, value_parser, Arg, ArgAction, Command};
|
||||
use file_config::FileConfig;
|
||||
use clap::parser::ValueSource;
|
||||
use clap::{arg, value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use log::LevelFilter;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
use watchers::config::defaults;
|
||||
use watchers::config::Config;
|
||||
use watchers::config::FileConfig;
|
||||
|
||||
pub struct Config {
|
||||
pub port: u32,
|
||||
pub host: String,
|
||||
pub idle_timeout: Duration,
|
||||
pub poll_time_idle: Duration,
|
||||
pub poll_time_window: Duration,
|
||||
pub idle_bucket_name: String,
|
||||
pub active_window_bucket_name: String,
|
||||
pub no_server: bool,
|
||||
pub verbosity: LevelFilter,
|
||||
filters: Vec<Filter>,
|
||||
pub fn setup_logger(verbosity: LevelFilter) -> Result<(), fern::InitError> {
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
let colors = ColoredLevelConfig::new()
|
||||
.info(Color::Green)
|
||||
.debug(Color::Blue)
|
||||
.trace(Color::Cyan);
|
||||
out.finish(format_args!(
|
||||
"[{} {} {}] {}",
|
||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.6f"),
|
||||
colors.color(record.level()),
|
||||
record.target(),
|
||||
message
|
||||
));
|
||||
})
|
||||
.level(log::LevelFilter::Error)
|
||||
.level_for("watchers", verbosity)
|
||||
.level_for("awatcher", verbosity)
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_cli() -> anyhow::Result<Self> {
|
||||
let matches = Command::new("Activity Watcher")
|
||||
.version("0.0.1")
|
||||
.about("A set of ActivityWatch desktop watchers")
|
||||
.args([
|
||||
arg!(-c --config <FILE> "Custom config file").value_parser(value_parser!(PathBuf)),
|
||||
arg!(--port <PORT> "Custom server port")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::port().to_string()),
|
||||
arg!(--host <HOST> "Custom server host")
|
||||
.value_parser(value_parser!(String))
|
||||
.default_value(defaults::host()),
|
||||
arg!(--"idle-timeout" <SECONDS> "Time of inactivity to consider the user idle")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::idle_timeout_seconds().to_string()),
|
||||
arg!(--"poll-time-idle" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::poll_time_idle_seconds().to_string()),
|
||||
arg!(--"poll-time-window" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::poll_time_window_seconds().to_string()),
|
||||
arg!(--"no-server" "Don't communicate to the ActivityWatch server")
|
||||
.value_parser(value_parser!(bool))
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("verbosity")
|
||||
.short('v')
|
||||
.help("Verbosity level: -v for warnings, -vv for info, -vvv for debug, -vvvv for trace")
|
||||
.action(ArgAction::Count),
|
||||
])
|
||||
.get_matches();
|
||||
pub fn from_cli() -> anyhow::Result<Config> {
|
||||
let matches = Command::new("Activity Watcher")
|
||||
.version("0.0.1")
|
||||
.about("A set of ActivityWatch desktop watchers")
|
||||
.args([
|
||||
arg!(-c --config <FILE> "Custom config file").value_parser(value_parser!(PathBuf)),
|
||||
arg!(--port <PORT> "Custom server port")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::port().to_string()),
|
||||
arg!(--host <HOST> "Custom server host")
|
||||
.value_parser(value_parser!(String))
|
||||
.default_value(defaults::host()),
|
||||
arg!(--"idle-timeout" <SECONDS> "Time of inactivity to consider the user idle")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::idle_timeout_seconds().to_string()),
|
||||
arg!(--"poll-time-idle" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::poll_time_idle_seconds().to_string()),
|
||||
arg!(--"poll-time-window" <SECONDS> "Period between sending heartbeats to the server for idle activity")
|
||||
.value_parser(value_parser!(u32))
|
||||
.default_value(defaults::poll_time_window_seconds().to_string()),
|
||||
arg!(--"no-server" "Don't communicate to the ActivityWatch server")
|
||||
.value_parser(value_parser!(bool))
|
||||
.action(ArgAction::SetTrue),
|
||||
Arg::new("verbosity")
|
||||
.short('v')
|
||||
.help("Verbosity level: -v for warnings, -vv for info, -vvv for debug, -vvvv for trace")
|
||||
.action(ArgAction::Count),
|
||||
])
|
||||
.get_matches();
|
||||
|
||||
let config = FileConfig::new_with_cli(&matches)?;
|
||||
let config = new_with_cli(&matches)?;
|
||||
|
||||
let hostname = gethostname::gethostname().into_string().unwrap();
|
||||
let idle_bucket_name = format!("aw-watcher-afk_{hostname}");
|
||||
let active_window_bucket_name = format!("aw-watcher-window_{hostname}");
|
||||
let verbosity = match matches.get_count("verbosity") {
|
||||
0 => LevelFilter::Error,
|
||||
1 => LevelFilter::Warn,
|
||||
2 => LevelFilter::Info,
|
||||
3 => LevelFilter::Debug,
|
||||
_ => LevelFilter::Trace,
|
||||
};
|
||||
let hostname = gethostname::gethostname().into_string().unwrap();
|
||||
let idle_bucket_name = format!("aw-watcher-afk_{hostname}");
|
||||
let active_window_bucket_name = format!("aw-watcher-window_{hostname}");
|
||||
let verbosity = match matches.get_count("verbosity") {
|
||||
0 => LevelFilter::Error,
|
||||
1 => LevelFilter::Warn,
|
||||
2 => LevelFilter::Info,
|
||||
3 => LevelFilter::Debug,
|
||||
_ => LevelFilter::Trace,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
port: config.server.port,
|
||||
host: config.server.host,
|
||||
idle_timeout: config.client.get_idle_timeout(),
|
||||
poll_time_idle: config.client.get_poll_time_idle(),
|
||||
poll_time_window: config.client.get_poll_time_window(),
|
||||
idle_bucket_name,
|
||||
active_window_bucket_name,
|
||||
filters: config.client.filters,
|
||||
no_server: *matches.get_one("no-server").unwrap(),
|
||||
verbosity,
|
||||
})
|
||||
}
|
||||
Ok(Config {
|
||||
port: config.server.port,
|
||||
host: config.server.host,
|
||||
idle_timeout: config.client.get_idle_timeout(),
|
||||
poll_time_idle: config.client.get_poll_time_idle(),
|
||||
poll_time_window: config.client.get_poll_time_window(),
|
||||
idle_bucket_name,
|
||||
active_window_bucket_name,
|
||||
filters: config.client.filters,
|
||||
no_server: *matches.get_one("no-server").unwrap(),
|
||||
verbosity,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn window_data_replacement(&self, app_id: &str, title: &str) -> Replacement {
|
||||
for filter in &self.filters {
|
||||
if let Some(replacement) = filter.replacement(app_id, title) {
|
||||
return replacement;
|
||||
pub fn new_with_cli(matches: &ArgMatches) -> anyhow::Result<FileConfig> {
|
||||
let mut config_path = None;
|
||||
if matches.contains_id("config") {
|
||||
let config_file = matches.get_one::<String>("config");
|
||||
if let Some(path) = config_file {
|
||||
if let Err(e) = std::fs::metadata(path) {
|
||||
warn!("Invalid config filename, using the default config: {e}");
|
||||
} else {
|
||||
config_path = Some(Path::new(path).to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut config = FileConfig::new(config_path)?;
|
||||
|
||||
Replacement::default()
|
||||
merge_cli(&mut config, matches);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn merge_cli(config: &mut FileConfig, matches: &ArgMatches) {
|
||||
get_arg_value(
|
||||
"poll-time-idle",
|
||||
matches,
|
||||
&mut config.client.poll_time_idle_seconds,
|
||||
);
|
||||
get_arg_value(
|
||||
"poll-time-window",
|
||||
matches,
|
||||
&mut config.client.poll_time_window_seconds,
|
||||
);
|
||||
get_arg_value(
|
||||
"idle-timeout",
|
||||
matches,
|
||||
&mut config.client.idle_timeout_seconds,
|
||||
);
|
||||
get_arg_value("port", matches, &mut config.server.port);
|
||||
get_arg_value("host", matches, &mut config.server.host);
|
||||
}
|
||||
|
||||
fn get_arg_value<T>(id: &str, matches: &ArgMatches, config_value: &mut T)
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
if let Some(ValueSource::CommandLine) = matches.value_source(id) {
|
||||
let value = &mut matches.get_one::<T>(id).unwrap().clone();
|
||||
std::mem::swap(config_value, value);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod config;
|
||||
pub mod report_client;
|
||||
pub mod watchers;
|
45
src/main.rs
45
src/main.rs
@ -4,41 +4,14 @@
|
||||
extern crate log;
|
||||
|
||||
mod config;
|
||||
mod report_client;
|
||||
mod watchers;
|
||||
|
||||
use config::Config;
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use log::LevelFilter;
|
||||
use report_client::ReportClient;
|
||||
use std::{sync::Arc, thread};
|
||||
use std::sync::Arc;
|
||||
use watchers::ConstructorFilter;
|
||||
|
||||
fn setup_logger(verbosity: LevelFilter) -> Result<(), fern::InitError> {
|
||||
fern::Dispatch::new()
|
||||
.format(|out, message, record| {
|
||||
let colors = ColoredLevelConfig::new()
|
||||
.info(Color::Green)
|
||||
.debug(Color::Blue)
|
||||
.trace(Color::Cyan);
|
||||
out.finish(format_args!(
|
||||
"[{} {} {}] {}",
|
||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.6f"),
|
||||
colors.color(record.level()),
|
||||
record.target(),
|
||||
message
|
||||
));
|
||||
})
|
||||
.level(log::LevelFilter::Error)
|
||||
.level_for("awatcher", verbosity)
|
||||
.chain(std::io::stdout())
|
||||
.apply()?;
|
||||
Ok(())
|
||||
}
|
||||
use watchers::ReportClient;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let config = Config::from_cli()?;
|
||||
setup_logger(config.verbosity)?;
|
||||
let config = config::from_cli()?;
|
||||
config::setup_logger(config.verbosity)?;
|
||||
|
||||
let client = ReportClient::new(config)?;
|
||||
let client = Arc::new(client);
|
||||
@ -69,19 +42,13 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
let mut thread_handlers = Vec::new();
|
||||
|
||||
let idle_watcher = watchers::IDLE.filter_first_supported();
|
||||
if let Some(mut watcher) = idle_watcher {
|
||||
let thread_client = Arc::clone(&client);
|
||||
let idle_handler = thread::spawn(move || watcher.watch(&thread_client));
|
||||
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client) {
|
||||
thread_handlers.push(idle_handler);
|
||||
} else {
|
||||
warn!("No supported idle handler is found");
|
||||
}
|
||||
|
||||
let window_watcher = watchers::ACTIVE_WINDOW.filter_first_supported();
|
||||
if let Some(mut watcher) = window_watcher {
|
||||
let thread_client = Arc::clone(&client);
|
||||
let active_window_handler = thread::spawn(move || watcher.watch(&thread_client));
|
||||
if let Some(active_window_handler) = watchers::ACTIVE_WINDOW.run_first_supported(&client) {
|
||||
thread_handlers.push(active_window_handler);
|
||||
} else {
|
||||
warn!("No supported active window handler is found");
|
||||
|
32
watchers/Cargo.toml
Normal file
32
watchers/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "watchers"
|
||||
version = "0.0.1"
|
||||
authors = ["Demmie <2e3s19@gmail.com>"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "watchers"
|
||||
crate-type = ["lib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
aw-client-rust = { git = "https://github.com/ActivityWatch/aw-server-rust" }
|
||||
wayland-client = "0.30.1"
|
||||
wayland-scanner = "0.30"
|
||||
wayland-backend = "0.1"
|
||||
x11rb = { version = "0.11.1", features = ["screensaver"] }
|
||||
zbus = {version = "3.11.1", optional = true}
|
||||
chrono = "0.4.24"
|
||||
toml = "0.7.3"
|
||||
dirs = "5.0.0"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_default = "0.1.0"
|
||||
serde_json = "1.0.95"
|
||||
regex = "1.8.1"
|
||||
log = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = ["gnome", "kwin_window"]
|
||||
gnome = ["zbus"]
|
||||
kwin_window = ["zbus"]
|
33
watchers/src/config.rs
Normal file
33
watchers/src/config.rs
Normal file
@ -0,0 +1,33 @@
|
||||
pub mod defaults;
|
||||
mod file_config;
|
||||
mod filters;
|
||||
|
||||
use self::filters::{Filter, Replacement};
|
||||
pub use file_config::FileConfig;
|
||||
use log::LevelFilter;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct Config {
|
||||
pub port: u32,
|
||||
pub host: String,
|
||||
pub idle_timeout: Duration,
|
||||
pub poll_time_idle: Duration,
|
||||
pub poll_time_window: Duration,
|
||||
pub idle_bucket_name: String,
|
||||
pub active_window_bucket_name: String,
|
||||
pub no_server: bool,
|
||||
pub verbosity: LevelFilter,
|
||||
pub filters: Vec<Filter>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn window_data_replacement(&self, app_id: &str, title: &str) -> Replacement {
|
||||
for filter in &self.filters {
|
||||
if let Some(replacement) = filter.replacement(app_id, title) {
|
||||
return replacement;
|
||||
}
|
||||
}
|
||||
|
||||
Replacement::default()
|
||||
}
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use clap::{parser::ValueSource, ArgMatches};
|
||||
use serde::Deserialize;
|
||||
use serde_default::DefaultFromSerde;
|
||||
use std::{
|
||||
io::ErrorKind,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{io::ErrorKind, path::PathBuf, time::Duration};
|
||||
|
||||
use crate::config::defaults;
|
||||
|
||||
@ -24,11 +19,11 @@ pub struct ServerConfig {
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct ClientConfig {
|
||||
#[serde(default = "defaults::idle_timeout_seconds")]
|
||||
idle_timeout_seconds: u32,
|
||||
pub idle_timeout_seconds: u32,
|
||||
#[serde(default = "defaults::poll_time_idle_seconds")]
|
||||
poll_time_idle_seconds: u32,
|
||||
pub poll_time_idle_seconds: u32,
|
||||
#[serde(default = "defaults::poll_time_window_seconds")]
|
||||
poll_time_window_seconds: u32,
|
||||
pub poll_time_window_seconds: u32,
|
||||
#[serde(default)]
|
||||
pub filters: Vec<Filter>,
|
||||
}
|
||||
@ -118,53 +113,4 @@ impl FileConfig {
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn new_with_cli(matches: &ArgMatches) -> anyhow::Result<Self> {
|
||||
let mut config_path = None;
|
||||
if matches.contains_id("config") {
|
||||
let config_file = matches.get_one::<String>("config");
|
||||
if let Some(path) = config_file {
|
||||
if let Err(e) = std::fs::metadata(path) {
|
||||
warn!("Invalid config filename, using the default config: {e}");
|
||||
} else {
|
||||
config_path = Some(Path::new(path).to_path_buf());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut config = Self::new(config_path)?;
|
||||
|
||||
config.merge_cli(matches);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn merge_cli(&mut self, matches: &ArgMatches) {
|
||||
get_arg_value(
|
||||
"poll-time-idle",
|
||||
matches,
|
||||
&mut self.client.poll_time_idle_seconds,
|
||||
);
|
||||
get_arg_value(
|
||||
"poll-time-window",
|
||||
matches,
|
||||
&mut self.client.poll_time_window_seconds,
|
||||
);
|
||||
get_arg_value(
|
||||
"idle-timeout",
|
||||
matches,
|
||||
&mut self.client.idle_timeout_seconds,
|
||||
);
|
||||
get_arg_value("port", matches, &mut self.server.port);
|
||||
get_arg_value("host", matches, &mut self.server.host);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_arg_value<T>(id: &str, matches: &ArgMatches, config_value: &mut T)
|
||||
where
|
||||
T: Clone + Send + Sync + 'static,
|
||||
{
|
||||
if let Some(ValueSource::CommandLine) = matches.value_source(id) {
|
||||
let value = &mut matches.get_one::<T>(id).unwrap().clone();
|
||||
std::mem::swap(config_value, value);
|
||||
}
|
||||
}
|
12
watchers/src/lib.rs
Normal file
12
watchers/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod config;
|
||||
mod report_client;
|
||||
mod watchers;
|
||||
|
||||
pub use report_client::ReportClient;
|
||||
pub use watchers::ConstructorFilter;
|
||||
pub use watchers::Watcher;
|
||||
pub use watchers::ACTIVE_WINDOW;
|
||||
pub use watchers::IDLE;
|
@ -14,7 +14,10 @@ mod x11_screensaver_idle;
|
||||
mod x11_window;
|
||||
|
||||
use crate::report_client::ReportClient;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
sync::Arc,
|
||||
thread::{self, JoinHandle},
|
||||
};
|
||||
|
||||
pub trait Watcher: Send {
|
||||
fn new() -> anyhow::Result<Self>
|
||||
@ -30,6 +33,8 @@ type WatcherConstructors = [WatcherConstructor];
|
||||
|
||||
pub trait ConstructorFilter {
|
||||
fn filter_first_supported(&self) -> Option<BoxedWatcher>;
|
||||
|
||||
fn run_first_supported(&self, client: &Arc<ReportClient>) -> Option<JoinHandle<()>>;
|
||||
}
|
||||
|
||||
impl ConstructorFilter for WatcherConstructors {
|
||||
@ -42,6 +47,17 @@ impl ConstructorFilter for WatcherConstructors {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn run_first_supported(&self, client: &Arc<ReportClient>) -> Option<JoinHandle<()>> {
|
||||
let idle_watcher = self.filter_first_supported();
|
||||
if let Some(mut watcher) = idle_watcher {
|
||||
let thread_client = Arc::clone(client);
|
||||
let idle_handler = thread::spawn(move || watcher.watch(&thread_client));
|
||||
Some(idle_handler)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! watcher {
|
Loading…
x
Reference in New Issue
Block a user