mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-06 11:35:46 +00:00
Catch signals to stop watchers
This commit is contained in:
parent
f1f22089a9
commit
cdc05e0d5f
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -458,6 +458,7 @@ dependencies = [
|
|||||||
"fern",
|
"fern",
|
||||||
"ksni",
|
"ksni",
|
||||||
"log",
|
"log",
|
||||||
|
"signal-hook",
|
||||||
"smol",
|
"smol",
|
||||||
"toml 0.7.3",
|
"toml 0.7.3",
|
||||||
"watchers",
|
"watchers",
|
||||||
|
@ -23,6 +23,7 @@ clap = { version = "4.2.1", features = ["string"] }
|
|||||||
fern = { version = "0.6.2", features = ["colored"] }
|
fern = { version = "0.6.2", features = ["colored"] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
signal-hook = "0.3.15"
|
||||||
|
|
||||||
ksni = {version = "0.2.0", optional = true}
|
ksni = {version = "0.2.0", optional = true}
|
||||||
aw-server = { git = "https://github.com/ActivityWatch/aw-server-rust.git", optional = true }
|
aw-server = { git = "https://github.com/ActivityWatch/aw-server-rust.git", optional = true }
|
||||||
|
12
src/main.rs
12
src/main.rs
@ -8,11 +8,16 @@ extern crate log;
|
|||||||
mod bundle;
|
mod bundle;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use watchers::ConstructorFilter;
|
use watchers::ConstructorFilter;
|
||||||
use watchers::ReportClient;
|
use watchers::ReportClient;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let is_stopped = Arc::new(AtomicBool::new(false));
|
||||||
|
signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&is_stopped))?;
|
||||||
|
signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&is_stopped))?;
|
||||||
|
|
||||||
let config = config::from_cli()?;
|
let config = config::from_cli()?;
|
||||||
let no_tray = config.no_tray;
|
let no_tray = config.no_tray;
|
||||||
let config = config.watchers_config;
|
let config = config.watchers_config;
|
||||||
@ -40,13 +45,16 @@ fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
let mut thread_handlers = Vec::new();
|
let mut thread_handlers = Vec::new();
|
||||||
|
|
||||||
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client) {
|
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client, Arc::clone(&is_stopped))
|
||||||
|
{
|
||||||
thread_handlers.push(idle_handler);
|
thread_handlers.push(idle_handler);
|
||||||
} else {
|
} else {
|
||||||
warn!("No supported idle handler is found");
|
warn!("No supported idle handler is found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(active_window_handler) = watchers::ACTIVE_WINDOW.run_first_supported(&client) {
|
if let Some(active_window_handler) =
|
||||||
|
watchers::ACTIVE_WINDOW.run_first_supported(&client, is_stopped)
|
||||||
|
{
|
||||||
thread_handlers.push(active_window_handler);
|
thread_handlers.push(active_window_handler);
|
||||||
} else {
|
} else {
|
||||||
warn!("No supported active window handler is found");
|
warn!("No supported active window handler is found");
|
||||||
|
@ -15,7 +15,7 @@ mod x11_window;
|
|||||||
|
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
sync::{atomic::AtomicBool, Arc},
|
||||||
thread::{self, JoinHandle},
|
thread::{self, JoinHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ pub trait Watcher: Send {
|
|||||||
fn new() -> anyhow::Result<Self>
|
fn new() -> anyhow::Result<Self>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>);
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>);
|
||||||
}
|
}
|
||||||
|
|
||||||
type BoxedWatcher = Box<dyn Watcher>;
|
type BoxedWatcher = Box<dyn Watcher>;
|
||||||
@ -34,7 +34,11 @@ type WatcherConstructors = [WatcherConstructor];
|
|||||||
pub trait ConstructorFilter {
|
pub trait ConstructorFilter {
|
||||||
fn filter_first_supported(&self) -> Option<BoxedWatcher>;
|
fn filter_first_supported(&self) -> Option<BoxedWatcher>;
|
||||||
|
|
||||||
fn run_first_supported(&self, client: &Arc<ReportClient>) -> Option<JoinHandle<()>>;
|
fn run_first_supported(
|
||||||
|
&self,
|
||||||
|
client: &Arc<ReportClient>,
|
||||||
|
is_stopped: Arc<AtomicBool>,
|
||||||
|
) -> Option<JoinHandle<()>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstructorFilter for WatcherConstructors {
|
impl ConstructorFilter for WatcherConstructors {
|
||||||
@ -48,11 +52,15 @@ impl ConstructorFilter for WatcherConstructors {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_first_supported(&self, client: &Arc<ReportClient>) -> Option<JoinHandle<()>> {
|
fn run_first_supported(
|
||||||
|
&self,
|
||||||
|
client: &Arc<ReportClient>,
|
||||||
|
is_stopped: Arc<AtomicBool>,
|
||||||
|
) -> Option<JoinHandle<()>> {
|
||||||
let idle_watcher = self.filter_first_supported();
|
let idle_watcher = self.filter_first_supported();
|
||||||
if let Some(mut watcher) = idle_watcher {
|
if let Some(mut watcher) = idle_watcher {
|
||||||
let thread_client = Arc::clone(client);
|
let thread_client = Arc::clone(client);
|
||||||
let idle_handler = thread::spawn(move || watcher.watch(&thread_client));
|
let idle_handler = thread::spawn(move || watcher.watch(&thread_client, is_stopped));
|
||||||
Some(idle_handler)
|
Some(idle_handler)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use super::{idle, Watcher};
|
use super::{idle, Watcher};
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use std::{sync::Arc, thread};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
use zbus::blocking::Connection;
|
use zbus::blocking::Connection;
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
@ -34,10 +36,14 @@ impl Watcher for IdleWatcher {
|
|||||||
Ok(watcher)
|
Ok(watcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
let mut is_idle = false;
|
let mut is_idle = false;
|
||||||
info!("Starting idle watcher");
|
info!("Starting idle watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
match idle::ping_since_last_input(self, is_idle, client) {
|
match idle::ping_since_last_input(self, is_idle, client) {
|
||||||
Ok(is_idle_again) => {
|
Ok(is_idle_again) => {
|
||||||
is_idle = is_idle_again;
|
is_idle = is_idle_again;
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{sync::Arc, thread};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
use zbus::blocking::Connection;
|
use zbus::blocking::Connection;
|
||||||
|
|
||||||
use super::Watcher;
|
use super::Watcher;
|
||||||
@ -85,9 +87,13 @@ impl Watcher for WindowWatcher {
|
|||||||
Ok(watcher)
|
Ok(watcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
info!("Starting active window watcher");
|
info!("Starting active window watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if let Err(error) = self.send_active_window(client) {
|
if let Err(error) = self.send_active_window(client) {
|
||||||
error!("Error on active window: {error}");
|
error!("Error on active window: {error}");
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use crate::report_client::ReportClient;
|
|||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use std::env::temp_dir;
|
use std::env::temp_dir;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{mpsc::channel, Arc, Mutex};
|
use std::sync::{mpsc::channel, Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use zbus::blocking::{Connection, ConnectionBuilder};
|
use zbus::blocking::{Connection, ConnectionBuilder};
|
||||||
@ -164,7 +165,7 @@ impl Watcher for WindowWatcher {
|
|||||||
Ok(Self { kwin_script })
|
Ok(Self { kwin_script })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
self.kwin_script.load().unwrap();
|
self.kwin_script.load().unwrap();
|
||||||
|
|
||||||
let active_window = Arc::new(Mutex::new(ActiveWindow {
|
let active_window = Arc::new(Mutex::new(ActiveWindow {
|
||||||
@ -200,6 +201,10 @@ impl Watcher for WindowWatcher {
|
|||||||
|
|
||||||
info!("Starting active window watcher");
|
info!("Starting active window watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if let Err(error) = send_active_window(client, &active_window) {
|
if let Err(error) = send_active_window(client, &active_window) {
|
||||||
error!("Error on sending active window: {error}");
|
error!("Error on sending active window: {error}");
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ use super::{wl_connection::subscribe_state, Watcher};
|
|||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::{sync::Arc, thread};
|
use std::{sync::Arc, thread};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
event_created_child, globals::GlobalListContents, protocol::wl_registry, Connection, Dispatch,
|
event_created_child, globals::GlobalListContents, protocol::wl_registry, Connection, Dispatch,
|
||||||
@ -141,7 +142,7 @@ impl Watcher for WindowWatcher {
|
|||||||
Ok(Self { connection })
|
Ok(Self { connection })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
let mut toplevel_state = ToplevelState::new(Arc::clone(client));
|
let mut toplevel_state = ToplevelState::new(Arc::clone(client));
|
||||||
|
|
||||||
self.connection
|
self.connection
|
||||||
@ -151,6 +152,10 @@ impl Watcher for WindowWatcher {
|
|||||||
|
|
||||||
info!("Starting wlr foreign toplevel watcher");
|
info!("Starting wlr foreign toplevel watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if let Err(e) = self.connection.event_queue.roundtrip(&mut toplevel_state) {
|
if let Err(e) = self.connection.event_queue.roundtrip(&mut toplevel_state) {
|
||||||
error!("Event queue is not processed: {e}");
|
error!("Event queue is not processed: {e}");
|
||||||
} else if let Err(e) = toplevel_state.send_active_window() {
|
} else if let Err(e) = toplevel_state.send_active_window() {
|
||||||
|
@ -3,6 +3,7 @@ use super::wl_connection::{subscribe_state, WlEventConnection};
|
|||||||
use super::Watcher;
|
use super::Watcher;
|
||||||
use crate::report_client::ReportClient;
|
use crate::report_client::ReportClient;
|
||||||
use chrono::{DateTime, Duration, Utc};
|
use chrono::{DateTime, Duration, Utc};
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::{sync::Arc, thread};
|
use std::{sync::Arc, thread};
|
||||||
use wayland_client::{
|
use wayland_client::{
|
||||||
globals::GlobalListContents,
|
globals::GlobalListContents,
|
||||||
@ -128,7 +129,7 @@ impl Watcher for IdleWatcher {
|
|||||||
Ok(Self { connection })
|
Ok(Self { connection })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
let timeout = u32::try_from(client.config.idle_timeout.as_secs() * 1000);
|
||||||
let mut idle_state = IdleState::new(
|
let mut idle_state = IdleState::new(
|
||||||
self.connection
|
self.connection
|
||||||
@ -143,6 +144,10 @@ impl Watcher for IdleWatcher {
|
|||||||
|
|
||||||
info!("Starting idle watcher");
|
info!("Starting idle watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if let Err(e) = self.connection.event_queue.roundtrip(&mut idle_state) {
|
if let Err(e) = self.connection.event_queue.roundtrip(&mut idle_state) {
|
||||||
error!("Event queue is not processed: {e}");
|
error!("Event queue is not processed: {e}");
|
||||||
} else if let Err(e) = idle_state.send_ping() {
|
} else if let Err(e) = idle_state.send_ping() {
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use super::{idle, x11_connection::X11Client, 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::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
pub struct IdleWatcher {
|
pub struct IdleWatcher {
|
||||||
client: X11Client,
|
client: X11Client,
|
||||||
@ -22,10 +24,14 @@ impl Watcher for IdleWatcher {
|
|||||||
Ok(IdleWatcher { client })
|
Ok(IdleWatcher { client })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
info!("Starting idle watcher");
|
info!("Starting idle watcher");
|
||||||
let mut is_idle = false;
|
let mut is_idle = false;
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
match idle::ping_since_last_input(self, is_idle, client) {
|
match idle::ping_since_last_input(self, is_idle, client) {
|
||||||
Ok(is_idle_again) => {
|
Ok(is_idle_again) => {
|
||||||
is_idle = is_idle_again;
|
is_idle = is_idle_again;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use super::{x11_connection::X11Client, 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::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
pub struct WindowWatcher {
|
pub struct WindowWatcher {
|
||||||
@ -40,9 +42,13 @@ impl Watcher for WindowWatcher {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn watch(&mut self, client: &std::sync::Arc<crate::report_client::ReportClient>) {
|
fn watch(&mut self, client: &Arc<ReportClient>, is_stopped: Arc<AtomicBool>) {
|
||||||
info!("Starting active window watcher");
|
info!("Starting active window watcher");
|
||||||
loop {
|
loop {
|
||||||
|
if is_stopped.load(Ordering::Relaxed) {
|
||||||
|
warn!("Received an exit signal, shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
if let Err(error) = self.send_active_window(client) {
|
if let Err(error) = self.send_active_window(client) {
|
||||||
error!("Error on sending active window: {error}");
|
error!("Error on sending active window: {error}");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user