main.rs (2058B)
1 use serde_json::json; 2 use std::io::Write; 3 use swayipc::{Connection, Event, EventType, Fallible, Node}; 4 5 /// True if this node represents an actual window (not a workspace/output/container) 6 fn is_window(node: &Node) -> bool { 7 node.app_id.is_some() || node.window_properties.is_some() 8 } 9 10 /// Walk the tree and find the focused node. 11 fn find_focused(node: &Node) -> Option<&Node> { 12 // Also checking for if it's a window otherwise Sway marks an empty workspace as focused 13 if node.focused && is_window(node) { 14 return Some(node); 15 } 16 node.nodes 17 .iter() 18 .chain(node.floating_nodes.iter()) 19 .find_map(find_focused) 20 } 21 22 fn format_line(node: &Node) -> String { 23 let app_id = node 24 .app_id 25 .clone() 26 .or_else(|| node.window_properties.as_ref()?.class.clone()) 27 .unwrap_or_else(|| "?".to_string()); 28 let title = node.name.clone().unwrap_or_default(); 29 format!("{app_id} | {title}") 30 } 31 32 fn print_json(text: &str) { 33 let escaped = json!({ "text": text, "tooltip": false }); 34 println!("{escaped}"); 35 let _ = std::io::stdout().flush(); 36 } 37 38 fn main() -> Fallible<()> { 39 // Print current focused window immediately so waybar has something 40 // on startup instead of waiting for the next event. 41 { 42 let mut conn = Connection::new()?; 43 let tree = conn.get_tree()?; 44 let text = find_focused(&tree).map(format_line).unwrap_or_default(); 45 print_json(&text); 46 } 47 48 // Then stream updates as they happen. 49 let subs = [EventType::Window, EventType::Workspace]; 50 for event in Connection::new()?.subscribe(subs)? { 51 match event { 52 Ok(Event::Window(_)) | Ok(Event::Workspace(_)) => { 53 let mut conn = Connection::new()?; 54 let tree = conn.get_tree()?; 55 let text = find_focused(&tree).map(format_line).unwrap_or_default(); 56 print_json(&text); 57 } 58 Ok(_) => {} 59 Err(e) => eprintln!("swayipc event error: {e}"), 60 } 61 } 62 63 Ok(()) 64 }