From 63bfe4181ecf7bca6c51e4b3c946898730138921 Mon Sep 17 00:00:00 2001 From: Demmie <2e3s19@gmail.com> Date: Sat, 6 May 2023 01:22:19 -0400 Subject: [PATCH] Extract watchers into module --- .github/workflows/verify.yml | 30 +-- Cargo.lock | 39 ++-- Cargo.toml | 32 ++- src/config.rs | 204 +++++++++++------- src/lib.rs | 6 - src/main.rs | 45 +--- watchers/Cargo.toml | 32 +++ watchers/src/config.rs | 33 +++ {src => watchers/src}/config/defaults.rs | 0 {src => watchers/src}/config/file_config.rs | 62 +----- {src => watchers/src}/config/filters.rs | 0 watchers/src/lib.rs | 12 ++ {src => watchers/src}/report_client.rs | 0 {src => watchers/src}/watchers.rs | 18 +- {src => watchers/src}/watchers/gnome_idle.rs | 0 .../src}/watchers/gnome_window.rs | 0 {src => watchers/src}/watchers/idle.rs | 0 {src => watchers/src}/watchers/kwin_window.js | 0 {src => watchers/src}/watchers/kwin_window.rs | 0 .../src}/watchers/wl-protocols/idle.xml | 0 ...oreign-toplevel-management-unstable-v1.xml | 0 {src => watchers/src}/watchers/wl_bindings.rs | 0 .../src}/watchers/wl_connection.rs | 0 .../src}/watchers/wl_foreign_toplevel.rs | 0 .../src}/watchers/wl_kwin_idle.rs | 0 .../src}/watchers/x11_connection.rs | 0 .../src}/watchers/x11_screensaver_idle.rs | 0 {src => watchers/src}/watchers/x11_window.rs | 0 28 files changed, 274 insertions(+), 239 deletions(-) delete mode 100644 src/lib.rs create mode 100644 watchers/Cargo.toml create mode 100644 watchers/src/config.rs rename {src => watchers/src}/config/defaults.rs (100%) rename {src => watchers/src}/config/file_config.rs (65%) rename {src => watchers/src}/config/filters.rs (100%) create mode 100644 watchers/src/lib.rs rename {src => watchers/src}/report_client.rs (100%) rename {src => watchers/src}/watchers.rs (76%) rename {src => watchers/src}/watchers/gnome_idle.rs (100%) rename {src => watchers/src}/watchers/gnome_window.rs (100%) rename {src => watchers/src}/watchers/idle.rs (100%) rename {src => watchers/src}/watchers/kwin_window.js (100%) rename {src => watchers/src}/watchers/kwin_window.rs (100%) rename {src => watchers/src}/watchers/wl-protocols/idle.xml (100%) rename {src => watchers/src}/watchers/wl-protocols/wlr-foreign-toplevel-management-unstable-v1.xml (100%) rename {src => watchers/src}/watchers/wl_bindings.rs (100%) rename {src => watchers/src}/watchers/wl_connection.rs (100%) rename {src => watchers/src}/watchers/wl_foreign_toplevel.rs (100%) rename {src => watchers/src}/watchers/wl_kwin_idle.rs (100%) rename {src => watchers/src}/watchers/x11_connection.rs (100%) rename {src => watchers/src}/watchers/x11_screensaver_idle.rs (100%) rename {src => watchers/src}/watchers/x11_window.rs (100%) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index d906371..6920bd7 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index a46f656..96a6dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 7b74fde..47c46d3 100644 --- a/Cargo.toml +++ b/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"] diff --git a/src/config.rs b/src/config.rs index 4a76db6..0f113f0 100644 --- a/src/config.rs +++ b/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, +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 { - let matches = Command::new("Activity Watcher") - .version("0.0.1") - .about("A set of ActivityWatch desktop watchers") - .args([ - arg!(-c --config "Custom config file").value_parser(value_parser!(PathBuf)), - arg!(--port "Custom server port") - .value_parser(value_parser!(u32)) - .default_value(defaults::port().to_string()), - arg!(--host "Custom server host") - .value_parser(value_parser!(String)) - .default_value(defaults::host()), - arg!(--"idle-timeout" "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" "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" "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 { + let matches = Command::new("Activity Watcher") + .version("0.0.1") + .about("A set of ActivityWatch desktop watchers") + .args([ + arg!(-c --config "Custom config file").value_parser(value_parser!(PathBuf)), + arg!(--port "Custom server port") + .value_parser(value_parser!(u32)) + .default_value(defaults::port().to_string()), + arg!(--host "Custom server host") + .value_parser(value_parser!(String)) + .default_value(defaults::host()), + arg!(--"idle-timeout" "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" "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" "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 { + let mut config_path = None; + if matches.contains_id("config") { + let config_file = matches.get_one::("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(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::(id).unwrap().clone(); + std::mem::swap(config_value, value); } } diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 360d577..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[macro_use] -extern crate log; - -pub mod config; -pub mod report_client; -pub mod watchers; diff --git a/src/main.rs b/src/main.rs index 404c788..f05da42 100644 --- a/src/main.rs +++ b/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"); diff --git a/watchers/Cargo.toml b/watchers/Cargo.toml new file mode 100644 index 0000000..4960f68 --- /dev/null +++ b/watchers/Cargo.toml @@ -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"] diff --git a/watchers/src/config.rs b/watchers/src/config.rs new file mode 100644 index 0000000..cc7ab09 --- /dev/null +++ b/watchers/src/config.rs @@ -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, +} + +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() + } +} diff --git a/src/config/defaults.rs b/watchers/src/config/defaults.rs similarity index 100% rename from src/config/defaults.rs rename to watchers/src/config/defaults.rs diff --git a/src/config/file_config.rs b/watchers/src/config/file_config.rs similarity index 65% rename from src/config/file_config.rs rename to watchers/src/config/file_config.rs index 2757818..404f125 100644 --- a/src/config/file_config.rs +++ b/watchers/src/config/file_config.rs @@ -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, } @@ -118,53 +113,4 @@ impl FileConfig { Ok(config) } - - pub fn new_with_cli(matches: &ArgMatches) -> anyhow::Result { - let mut config_path = None; - if matches.contains_id("config") { - let config_file = matches.get_one::("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(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::(id).unwrap().clone(); - std::mem::swap(config_value, value); - } } diff --git a/src/config/filters.rs b/watchers/src/config/filters.rs similarity index 100% rename from src/config/filters.rs rename to watchers/src/config/filters.rs diff --git a/watchers/src/lib.rs b/watchers/src/lib.rs new file mode 100644 index 0000000..3ac420e --- /dev/null +++ b/watchers/src/lib.rs @@ -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; diff --git a/src/report_client.rs b/watchers/src/report_client.rs similarity index 100% rename from src/report_client.rs rename to watchers/src/report_client.rs diff --git a/src/watchers.rs b/watchers/src/watchers.rs similarity index 76% rename from src/watchers.rs rename to watchers/src/watchers.rs index 9eb1c99..83c7b69 100644 --- a/src/watchers.rs +++ b/watchers/src/watchers.rs @@ -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 @@ -30,6 +33,8 @@ type WatcherConstructors = [WatcherConstructor]; pub trait ConstructorFilter { fn filter_first_supported(&self) -> Option; + + fn run_first_supported(&self, client: &Arc) -> Option>; } impl ConstructorFilter for WatcherConstructors { @@ -42,6 +47,17 @@ impl ConstructorFilter for WatcherConstructors { } }) } + + fn run_first_supported(&self, client: &Arc) -> Option> { + 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 { diff --git a/src/watchers/gnome_idle.rs b/watchers/src/watchers/gnome_idle.rs similarity index 100% rename from src/watchers/gnome_idle.rs rename to watchers/src/watchers/gnome_idle.rs diff --git a/src/watchers/gnome_window.rs b/watchers/src/watchers/gnome_window.rs similarity index 100% rename from src/watchers/gnome_window.rs rename to watchers/src/watchers/gnome_window.rs diff --git a/src/watchers/idle.rs b/watchers/src/watchers/idle.rs similarity index 100% rename from src/watchers/idle.rs rename to watchers/src/watchers/idle.rs diff --git a/src/watchers/kwin_window.js b/watchers/src/watchers/kwin_window.js similarity index 100% rename from src/watchers/kwin_window.js rename to watchers/src/watchers/kwin_window.js diff --git a/src/watchers/kwin_window.rs b/watchers/src/watchers/kwin_window.rs similarity index 100% rename from src/watchers/kwin_window.rs rename to watchers/src/watchers/kwin_window.rs diff --git a/src/watchers/wl-protocols/idle.xml b/watchers/src/watchers/wl-protocols/idle.xml similarity index 100% rename from src/watchers/wl-protocols/idle.xml rename to watchers/src/watchers/wl-protocols/idle.xml diff --git a/src/watchers/wl-protocols/wlr-foreign-toplevel-management-unstable-v1.xml b/watchers/src/watchers/wl-protocols/wlr-foreign-toplevel-management-unstable-v1.xml similarity index 100% rename from src/watchers/wl-protocols/wlr-foreign-toplevel-management-unstable-v1.xml rename to watchers/src/watchers/wl-protocols/wlr-foreign-toplevel-management-unstable-v1.xml diff --git a/src/watchers/wl_bindings.rs b/watchers/src/watchers/wl_bindings.rs similarity index 100% rename from src/watchers/wl_bindings.rs rename to watchers/src/watchers/wl_bindings.rs diff --git a/src/watchers/wl_connection.rs b/watchers/src/watchers/wl_connection.rs similarity index 100% rename from src/watchers/wl_connection.rs rename to watchers/src/watchers/wl_connection.rs diff --git a/src/watchers/wl_foreign_toplevel.rs b/watchers/src/watchers/wl_foreign_toplevel.rs similarity index 100% rename from src/watchers/wl_foreign_toplevel.rs rename to watchers/src/watchers/wl_foreign_toplevel.rs diff --git a/src/watchers/wl_kwin_idle.rs b/watchers/src/watchers/wl_kwin_idle.rs similarity index 100% rename from src/watchers/wl_kwin_idle.rs rename to watchers/src/watchers/wl_kwin_idle.rs diff --git a/src/watchers/x11_connection.rs b/watchers/src/watchers/x11_connection.rs similarity index 100% rename from src/watchers/x11_connection.rs rename to watchers/src/watchers/x11_connection.rs diff --git a/src/watchers/x11_screensaver_idle.rs b/watchers/src/watchers/x11_screensaver_idle.rs similarity index 100% rename from src/watchers/x11_screensaver_idle.rs rename to watchers/src/watchers/x11_screensaver_idle.rs diff --git a/src/watchers/x11_window.rs b/watchers/src/watchers/x11_window.rs similarity index 100% rename from src/watchers/x11_window.rs rename to watchers/src/watchers/x11_window.rs