use serde_json::json; use std::io::Write; use swayipc::{Connection, Event, EventType, Fallible, Node}; /// True if this node represents an actual window (not a workspace/output/container) fn is_window(node: &Node) -> bool { node.app_id.is_some() || node.window_properties.is_some() } /// Walk the tree and find the focused node. fn find_focused(node: &Node) -> Option<&Node> { // Also checking for if it's a window otherwise Sway marks an empty workspace as focused if node.focused && is_window(node) { return Some(node); } node.nodes .iter() .chain(node.floating_nodes.iter()) .find_map(find_focused) } fn format_line(node: &Node) -> String { let app_id = node .app_id .clone() .or_else(|| node.window_properties.as_ref()?.class.clone()) .unwrap_or_else(|| "?".to_string()); let title = node.name.clone().unwrap_or_default(); format!("{app_id} | {title}") } fn print_json(text: &str) { let escaped = json!({ "text": text, "tooltip": false }); println!("{escaped}"); let _ = std::io::stdout().flush(); } fn main() -> Fallible<()> { // Print current focused window immediately so waybar has something // on startup instead of waiting for the next event. { let mut conn = Connection::new()?; let tree = conn.get_tree()?; let text = find_focused(&tree).map(format_line).unwrap_or_default(); print_json(&text); } // Then stream updates as they happen. let subs = [EventType::Window, EventType::Workspace]; for event in Connection::new()?.subscribe(subs)? { match event { Ok(Event::Window(_)) | Ok(Event::Workspace(_)) => { let mut conn = Connection::new()?; let tree = conn.get_tree()?; let text = find_focused(&tree).map(format_line).unwrap_or_default(); print_json(&text); } Ok(_) => {} Err(e) => eprintln!("swayipc event error: {e}"), } } Ok(()) }