use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::time::Instant;
use winshift::{FocusChangeHandler, WindowFocusHook};
struct ExampleHandler {
current_app_pid: Arc<RwLock<i32>>,
current_window: Arc<RwLock<String>>,
last_change: Arc<RwLock<Instant>>,
app_tracking: Arc<RwLock<HashMap<i32, (String, u32)>>>, }
impl ExampleHandler {
fn new() -> Self {
Self {
current_app_pid: Arc::new(RwLock::new(0)),
current_window: Arc::new(RwLock::new(String::new())),
last_change: Arc::new(RwLock::new(Instant::now())),
app_tracking: Arc::new(RwLock::new(HashMap::new())),
}
}
}
impl FocusChangeHandler for ExampleHandler {
fn on_app_change(&self, pid: i32, app_name: String) {
let mut current_pid = self.current_app_pid.write().unwrap();
let mut last_change = self.last_change.write().unwrap();
let now = Instant::now();
*last_change = now;
if *current_pid != pid {
if *current_pid == 0 {
println!("Initial app: {app_name} (PID: {pid})");
} else {
println!(
"Application switched: PID {} -> {} (PID: {})",
*current_pid, app_name, pid
);
let mut tracking = self.app_tracking.write().unwrap();
let (_, count) = tracking.entry(pid).or_insert((app_name.clone(), 0));
*count += 1;
println!(" {app_name} has been activated {count} time(s)");
}
*current_pid = pid;
}
}
fn on_window_change(&self, window_title: String) {
let mut current = self.current_window.write().unwrap();
let mut last_change = self.last_change.write().unwrap();
let now = Instant::now();
*last_change = now;
if window_title.is_empty() {
println!("Warning: Received empty window title");
return;
}
if *current != window_title {
if current.is_empty() {
println!("Initial window: '{window_title}'");
} else {
println!("Window changed: '{current}' -> '{window_title}'");
}
*current = window_title;
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
winshift::env_logger::init();
println!("This monitor detects both application switches and window changes.");
println!();
println!("Try switching between:");
println!(" - Different applications (Safari, Terminal, VSCode, etc.)");
println!(" - Different windows within the same app");
println!();
println!("Press Ctrl+C to exit");
println!();
let handler = ExampleHandler::new();
let hook = Arc::new(WindowFocusHook::new(handler));
let stop_handle = hook.clone();
::ctrlc::set_handler(move || {
println!("\nShutting down monitor...");
eprintln!("[SIGNAL] Ctrl+C signal received!");
if let Err(err) = stop_handle.stop() {
eprintln!("Failed to stop hook: {err}");
}
eprintln!("[SIGNAL] Stop signal sent, exiting signal handler");
})?;
if let Err(e) = hook.run() {
eprintln!("Error running hook: {e}");
}
println!("Monitor stopped.");
Ok(())
}