Aggregate into Watcher trait

This commit is contained in:
Demmie 2023-04-13 00:46:32 -04:00
parent 91224d63e8
commit 32f0b5bccc
No known key found for this signature in database
GPG Key ID: B06DAA3D432C6E9A
4 changed files with 150 additions and 81 deletions

View File

@ -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();
}
}

View File

@ -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, ()))
}
}

View File

@ -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,
)));
}
}

View File

@ -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,
)));
}
}