use crate::ipc; use crate::util; use serde_json::Deserializer; use std::collections::HashMap; use std::io::Write; use std::os::unix::net::{UnixListener, UnixStream}; use std::process as proc; use std::sync::Arc; use std::sync::RwLock; use std::thread; use std::time::{SystemTime, UNIX_EPOCH}; pub fn monitor_con_events( con_props: Arc>>, ) { let mut child = proc::Command::new("swaymsg") .arg("--monitor") .arg("--raw") .arg("-t") .arg("subscribe") .arg("[\"window\", \"workspace\"]") .stdout(proc::Stdio::piped()) .spawn() .expect("Failed to subscribe to window events"); let stdout = child.stdout.take().unwrap(); let reader = std::io::BufReader::new(stdout); let deserializer = Deserializer::from_reader(reader); for res in deserializer.into_iter::() { match res { Ok(win_ev) => handle_con_event(win_ev, con_props.clone()), Err(err) => eprintln!("Error handling window event:\n{:?}", err), } } match child.try_wait() { Ok(exit_code) => match exit_code { None => { eprintln!("Stopped monitoring con events. Restarting..."); monitor_con_events(con_props) } Some(exit_code) => { println!("Swaymsg exited with code {}. Exiting.", exit_code) } }, Err(err) => println!("Swaymsg errored with {}. Exiting.", err), } } fn update_last_focus_time( id: ipc::Id, con_props: Arc>>, ) { let mut write_lock = con_props.write().unwrap(); if let Some(mut wp) = write_lock.get_mut(&id) { wp.last_focus_time = get_epoch_time_as_millis(); } else { write_lock.insert( id, ipc::ConProps { last_focus_time: get_epoch_time_as_millis(), }, ); } } fn remove_con_props( id: ipc::Id, con_props: Arc>>, ) { con_props.write().unwrap().remove(&id); } fn handle_con_event( ev: ipc::ConEvent, con_props: Arc>>, ) { let mut handled = true; let con_props2 = con_props.clone(); match ev { ipc::ConEvent::WindowEvent { change, container } => match change { ipc::WindowEventType::New | ipc::WindowEventType::Focus => { update_last_focus_time(container.id, con_props) } ipc::WindowEventType::Close => { remove_con_props(container.id, con_props) } _ => handled = false, }, ipc::ConEvent::WorkspaceEvent { change, current, old: _, } => match change { ipc::WorkspaceEventType::Init | ipc::WorkspaceEventType::Focus => { update_last_focus_time( current .expect("No current in Init or Focus workspace event") .id, con_props, ) } ipc::WorkspaceEventType::Empty => remove_con_props( current.expect("No current in Empty workspace event").id, con_props, ), _ => handled = false, }, } if handled { println!("New con_props state:\n{:#?}", *con_props2.read().unwrap()); } } fn get_epoch_time_as_millis() -> u128 { SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Couldn't get epoch time!") .as_millis() } pub fn serve_client_requests( con_props: Arc>>, ) -> std::io::Result<()> { match std::fs::remove_file(util::get_swayr_socket_path()) { Ok(()) => println!("Deleted stale socket from previous run."), Err(e) => eprintln!("Could not delete socket:\n{:?}", e), } let listener = UnixListener::bind(util::get_swayr_socket_path())?; for stream in listener.incoming() { match stream { Ok(stream) => { let wp_clone = con_props.clone(); thread::spawn(move || handle_client_request(stream, wp_clone)); } Err(err) => return Err(err), } } Ok(()) } fn handle_client_request( mut stream: UnixStream, con_props: Arc>>, ) { let json = serde_json::to_string(&*con_props.read().unwrap()).unwrap(); if let Err(err) = stream.write_all(json.as_bytes()) { eprintln!("Error writing to client: {:?}", err); } }