mirror of
https://github.com/2e3s/awatcher.git
synced 2025-06-05 19:15:33 +00:00
Aggregate into Watcher trait
This commit is contained in:
parent
91224d63e8
commit
32f0b5bccc
53
src/main.rs
53
src/main.rs
@ -15,8 +15,8 @@ use fern::colors::{Color, ColoredLevelConfig};
|
||||
use report_client::ReportClient;
|
||||
use std::env;
|
||||
use std::{error::Error, str::FromStr, sync::Arc, thread};
|
||||
use wl_kwin_idle::run as run_kwin_idle;
|
||||
use wl_kwin_window::run as run_kwin_active_window;
|
||||
use wl_kwin_idle::KwinIdleWatcher;
|
||||
use wl_kwin_window::KwinWindowWatcher;
|
||||
|
||||
type BoxedError = Box<dyn Error>;
|
||||
|
||||
@ -47,6 +47,14 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type WatcherConstructor = fn() -> Result<Box<dyn Watcher>, BoxedError>;
|
||||
trait Watcher: Send {
|
||||
fn new() -> Result<Self, BoxedError>
|
||||
where
|
||||
Self: Sized;
|
||||
fn watch(&mut self, client: &Arc<ReportClient>);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
setup_logger().unwrap();
|
||||
|
||||
@ -60,14 +68,39 @@ fn main() {
|
||||
info!("Idle timeout: {} seconds", client.config.idle_timeout);
|
||||
info!("Polling period: {} seconds", client.config.poll_time_idle);
|
||||
|
||||
let client1 = Arc::clone(&client);
|
||||
let idle_handler = thread::spawn(move || run_kwin_idle(&client1));
|
||||
let mut thread_handlers = Vec::new();
|
||||
let idle_watchers: Vec<WatcherConstructor> = vec![|| Ok(Box::new(KwinIdleWatcher::new()?))];
|
||||
let window_watchers: Vec<WatcherConstructor> = vec![|| Ok(Box::new(KwinWindowWatcher::new()?))];
|
||||
|
||||
let client2 = Arc::clone(&client);
|
||||
let active_window_handler = thread::spawn(move || run_kwin_active_window(&client2));
|
||||
let filter_watcher = |watchers: Vec<WatcherConstructor>| {
|
||||
watchers.iter().find_map(|watcher| match watcher() {
|
||||
Ok(watcher) => Some(watcher),
|
||||
Err(e) => {
|
||||
info!("Watcher cannot run: {e}");
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
idle_handler.join().expect("Idle thread failed");
|
||||
active_window_handler
|
||||
.join()
|
||||
.expect("Active window thread failed");
|
||||
let idle_watcher = filter_watcher(idle_watchers);
|
||||
if let Some(mut watcher) = idle_watcher {
|
||||
let thread_client = Arc::clone(&client);
|
||||
let idle_handler = thread::spawn(move || watcher.watch(&thread_client));
|
||||
thread_handlers.push(idle_handler);
|
||||
} else {
|
||||
warn!("No supported idle handler is found");
|
||||
}
|
||||
|
||||
let window_watcher = filter_watcher(window_watchers);
|
||||
if let Some(mut watcher) = window_watcher {
|
||||
let thread_client = Arc::clone(&client);
|
||||
let active_window_handler = thread::spawn(move || watcher.watch(&thread_client));
|
||||
thread_handlers.push(active_window_handler);
|
||||
} else {
|
||||
warn!("No supported active window handler is found");
|
||||
}
|
||||
|
||||
for handler in thread_handlers {
|
||||
handler.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,20 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_idle_timeout(&self, timeout: u32) -> Result<OrgKdeKwinIdleTimeout, BoxedError>
|
||||
pub fn get_kwin_idle(&self) -> Result<OrgKdeKwinIdle, BoxedError>
|
||||
where
|
||||
T: Dispatch<OrgKdeKwinIdle, ()>,
|
||||
{
|
||||
self.globals
|
||||
.bind::<OrgKdeKwinIdle, T, ()>(
|
||||
&self.queue_handle,
|
||||
1..=OrgKdeKwinIdle::interface().version,
|
||||
(),
|
||||
)
|
||||
.map_err(std::convert::Into::into)
|
||||
}
|
||||
|
||||
pub fn get_kwin_idle_timeout(&self, timeout: u32) -> Result<OrgKdeKwinIdleTimeout, BoxedError>
|
||||
where
|
||||
T: Dispatch<OrgKdeKwinIdle, ()>
|
||||
+ Dispatch<OrgKdeKwinIdleTimeout, ()>
|
||||
@ -47,11 +60,7 @@ where
|
||||
self.globals
|
||||
.bind(&self.queue_handle, 1..=WlSeat::interface().version, ())?;
|
||||
|
||||
let idle: OrgKdeKwinIdle = self.globals.bind(
|
||||
&self.queue_handle,
|
||||
1..=OrgKdeKwinIdle::interface().version,
|
||||
(),
|
||||
)?;
|
||||
let idle = self.get_kwin_idle()?;
|
||||
Ok(idle.get_idle_timeout(&seat, timeout, &self.queue_handle, ()))
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::Watcher;
|
||||
|
||||
use super::report_client::ReportClient;
|
||||
use super::wl_bindings;
|
||||
use super::wl_connection::WlEventConnection;
|
||||
@ -60,7 +62,6 @@ impl IdleState {
|
||||
}
|
||||
|
||||
fn run_loop(&mut self, connection: &mut WlEventConnection<Self>) -> Result<(), BoxedError> {
|
||||
// connection.event_queue.blocking_dispatch(self).unwrap();
|
||||
connection.event_queue.roundtrip(self).unwrap();
|
||||
let now = Utc::now();
|
||||
if !self.is_idle {
|
||||
@ -158,32 +159,46 @@ impl Dispatch<OrgKdeKwinIdleTimeout, ()> for IdleState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(client: &Arc<ReportClient>) {
|
||||
let bucket_name = format!(
|
||||
"aw-watcher-afk_{}",
|
||||
gethostname::gethostname().into_string().unwrap()
|
||||
);
|
||||
pub struct KwinIdleWatcher {
|
||||
connection: WlEventConnection<IdleState>,
|
||||
}
|
||||
|
||||
client.create_bucket(&bucket_name, "afkstatus").unwrap();
|
||||
impl Watcher for KwinIdleWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
let connection: WlEventConnection<IdleState> = WlEventConnection::connect()?;
|
||||
connection.get_kwin_idle()?;
|
||||
|
||||
let mut connection = WlEventConnection::connect().unwrap();
|
||||
Ok(Self { connection })
|
||||
}
|
||||
|
||||
let mut idle_state = IdleState::new(
|
||||
connection
|
||||
.get_idle_timeout(client.config.idle_timeout * 1000)
|
||||
.unwrap(),
|
||||
Arc::clone(client),
|
||||
bucket_name,
|
||||
);
|
||||
connection.event_queue.roundtrip(&mut idle_state).unwrap();
|
||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
||||
let bucket_name = format!(
|
||||
"aw-watcher-afk_{}",
|
||||
gethostname::gethostname().into_string().unwrap()
|
||||
);
|
||||
|
||||
info!("Starting idle watcher");
|
||||
loop {
|
||||
if let Err(e) = idle_state.run_loop(&mut connection) {
|
||||
error!("Error on idle iteration {e}");
|
||||
client.create_bucket(&bucket_name, "afkstatus").unwrap();
|
||||
|
||||
let mut idle_state = IdleState::new(
|
||||
self.connection
|
||||
.get_kwin_idle_timeout(client.config.idle_timeout * 1000)
|
||||
.unwrap(),
|
||||
Arc::clone(client),
|
||||
bucket_name,
|
||||
);
|
||||
self.connection
|
||||
.event_queue
|
||||
.roundtrip(&mut idle_state)
|
||||
.unwrap();
|
||||
|
||||
info!("Starting idle watcher");
|
||||
loop {
|
||||
if let Err(e) = idle_state.run_loop(&mut self.connection) {
|
||||
error!("Error on idle iteration {e}");
|
||||
}
|
||||
thread::sleep(time::Duration::from_secs(u64::from(
|
||||
client.config.poll_time_idle,
|
||||
)));
|
||||
}
|
||||
thread::sleep(time::Duration::from_secs(u64::from(
|
||||
client.config.poll_time_idle,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::Watcher;
|
||||
|
||||
/*
|
||||
* This uses a hack with KWin scripts in order to receive the active window.
|
||||
* For the moment of writing, KWin doesn't implement the appropriate protocols to get a top level window.
|
||||
@ -29,11 +31,6 @@ impl KWinScript {
|
||||
}
|
||||
|
||||
fn load(&self) -> Result<(), BoxedError> {
|
||||
if self.is_loaded()? {
|
||||
warn!("KWin script is already loaded, unloading");
|
||||
self.unload().unwrap();
|
||||
}
|
||||
|
||||
let path = temp_dir().join("kwin_window.js");
|
||||
std::fs::write(&path, KWIN_SCRIPT).unwrap();
|
||||
|
||||
@ -168,49 +165,64 @@ impl ActiveWindowInterface {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(client: &Arc<ReportClient>) {
|
||||
let hostname = gethostname::gethostname().into_string().unwrap();
|
||||
let bucket_name = format!("aw-watcher-window_{hostname}");
|
||||
let kwin_script = KWinScript::new(Connection::session().unwrap());
|
||||
pub struct KwinWindowWatcher {
|
||||
kwin_script: KWinScript,
|
||||
}
|
||||
|
||||
kwin_script.load().unwrap();
|
||||
impl Watcher for KwinWindowWatcher {
|
||||
fn new() -> Result<Self, BoxedError> {
|
||||
let kwin_script = KWinScript::new(Connection::session()?);
|
||||
if kwin_script.is_loaded()? {
|
||||
warn!("KWin script is already loaded, unloading");
|
||||
kwin_script.unload()?;
|
||||
}
|
||||
|
||||
let active_window = Arc::new(Mutex::new(ActiveWindow {
|
||||
caption: String::new(),
|
||||
resource_name: String::new(),
|
||||
resource_class: String::new(),
|
||||
}));
|
||||
let active_window_interface = ActiveWindowInterface {
|
||||
active_window: Arc::clone(&active_window),
|
||||
};
|
||||
Ok(Self { kwin_script })
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
thread::spawn(move || {
|
||||
let result = (|| {
|
||||
ConnectionBuilder::session()?
|
||||
.name("com._2e3s.Awatcher")?
|
||||
.serve_at("/com/_2e3s/Awatcher", active_window_interface)?
|
||||
.build()
|
||||
})();
|
||||
match result {
|
||||
Ok(connection) => {
|
||||
tx.send(Ok(())).unwrap();
|
||||
loop {
|
||||
connection.monitor_activity().wait();
|
||||
fn watch(&mut self, client: &Arc<ReportClient>) {
|
||||
let hostname = gethostname::gethostname().into_string().unwrap();
|
||||
let bucket_name = format!("aw-watcher-window_{hostname}");
|
||||
|
||||
self.kwin_script.load().unwrap();
|
||||
|
||||
let active_window = Arc::new(Mutex::new(ActiveWindow {
|
||||
caption: String::new(),
|
||||
resource_name: String::new(),
|
||||
resource_class: String::new(),
|
||||
}));
|
||||
let active_window_interface = ActiveWindowInterface {
|
||||
active_window: Arc::clone(&active_window),
|
||||
};
|
||||
|
||||
let (tx, rx) = channel();
|
||||
thread::spawn(move || {
|
||||
let result = (|| {
|
||||
ConnectionBuilder::session()?
|
||||
.name("com._2e3s.Awatcher")?
|
||||
.serve_at("/com/_2e3s/Awatcher", active_window_interface)?
|
||||
.build()
|
||||
})();
|
||||
match result {
|
||||
Ok(connection) => {
|
||||
tx.send(Ok(())).unwrap();
|
||||
loop {
|
||||
connection.monitor_activity().wait();
|
||||
}
|
||||
}
|
||||
Err(e) => tx.send(Err(e)),
|
||||
}
|
||||
Err(e) => tx.send(Err(e)),
|
||||
}
|
||||
});
|
||||
let _ = rx.recv().unwrap();
|
||||
});
|
||||
let _ = rx.recv().unwrap();
|
||||
|
||||
info!("Starting active window watcher");
|
||||
loop {
|
||||
if let Err(error) = send_heartbeat(client, &bucket_name, &active_window) {
|
||||
error!("Error on sending active window heartbeat: {error}");
|
||||
info!("Starting active window watcher");
|
||||
loop {
|
||||
if let Err(error) = send_heartbeat(client, &bucket_name, &active_window) {
|
||||
error!("Error on sending active window heartbeat: {error}");
|
||||
}
|
||||
thread::sleep(time::Duration::from_secs(u64::from(
|
||||
client.config.poll_time_window,
|
||||
)));
|
||||
}
|
||||
thread::sleep(time::Duration::from_secs(u64::from(
|
||||
client.config.poll_time_window,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user