use rdev::{Event, EventType, ListenError, listen};
use serde::Deserialize;
use std::{
process::Command,
sync::{Arc, Mutex},
time::{Duration, Instant},
};
#[derive(Deserialize)]
pub struct Frame {
pub x: f64,
pub y: f64,
pub w: f64,
pub h: f64,
}
#[derive(Deserialize)]
pub struct YabaiWindow {
pub id: u32,
pub frame: Frame,
}
pub fn run_focus_listener() -> Result<(), ListenError> {
let last_focus = Arc::new(Mutex::new(Instant::now() - Duration::from_secs(1)));
let last_clone = Arc::clone(&last_focus);
listen(move |event: Event| {
if let EventType::MouseMove { x, y } = event.event_type {
let mut last = last_clone.lock().unwrap();
if last.elapsed() < Duration::from_millis(50) {
return;
}
*last = Instant::now();
if let Ok(out) = Command::new("yabai")
.args(&["-m", "query", "--windows"])
.output()
{
if !out.status.success() {
return;
}
if let Ok(wins) = serde_json::from_slice::<Vec<YabaiWindow>>(&out.stdout) {
let px = x as f64;
let py = y as f64;
if let Some(w) = wins.iter().find(|w| {
px >= w.frame.x
&& px <= w.frame.x + w.frame.w
&& py >= w.frame.y
&& py <= w.frame.y + w.frame.h
}) {
let _ = Command::new("yabai")
.args(&["-m", "window", "--focus", &w.id.to_string()])
.status();
}
}
}
}
})?;
Ok(())
}