diff --git a/src/bin/swayr.rs b/src/bin/swayr.rs index 4b96ef8..d44ac0a 100644 --- a/src/bin/swayr.rs +++ b/src/bin/swayr.rs @@ -18,13 +18,16 @@ struct Opts { #[derive(Clap)] enum SwayrCommand { - /// Switch window using wofi (urgent first, then LRU order, focused last) + /// Switch window with display order urgent first, then LRU order, focused last SwitchWindow, + /// Quit a window with display order focused first, then reverse-LRU order, urgent last + QuitWindow, } fn main() { let opts: Opts = Opts::parse(); match opts.command { SwayrCommand::SwitchWindow => client::switch_window(), + SwayrCommand::QuitWindow => client::quit_window(), } } diff --git a/src/client.rs b/src/client.rs index c23ec5e..66c67da 100644 --- a/src/client.rs +++ b/src/client.rs @@ -12,15 +12,24 @@ fn get_window_props() -> Result, serde_json:: } } +fn get_windows(root_node: &ipc::Node) -> std::vec::Vec { + let win_props = match get_window_props() { + Ok(win_props) => Some(win_props), + Err(e) => { + eprintln!("Got no win_props: {:?}", e); + None + } + }; + + window::get_windows(&root_node, win_props.unwrap_or(HashMap::new())) +} + pub fn switch_window() { let root_node = get_tree(); - let mut windows = window::get_windows(&root_node); - match get_window_props() { - Ok(win_props) => window::sort_windows(&mut windows, win_props), - Err(e) => eprintln!("Got no win_props: {:?}", e), - } + let mut windows = get_windows(&root_node); + windows.sort(); - if let Some(window) = util::select_window(&windows) { + if let Some(window) = util::select_window("Switch to window", &windows) { util::swaymsg(vec![ format!("[con_id={}]", window.node.id).as_str(), "focus", @@ -28,7 +37,20 @@ pub fn switch_window() { } } -pub fn get_tree() -> ipc::Node { +pub fn quit_window() { + let root_node = get_tree(); + let mut windows = get_windows(&root_node); + windows.sort_by(|a, b| a.cmp(b).reverse()); + + if let Some(window) = util::select_window("Quit window", &windows) { + util::swaymsg(vec![ + format!("[con_id={}]", window.node.id).as_str(), + "kill", + ]); + } +} + +fn get_tree() -> ipc::Node { let output = util::swaymsg(vec!["-t", "get_tree"]); let result = serde_json::from_str(output.as_str()); diff --git a/src/demon.rs b/src/demon.rs index 3ee957d..c031229 100644 --- a/src/demon.rs +++ b/src/demon.rs @@ -34,12 +34,6 @@ fn handle_window_event( ev: ipc::WindowEvent, win_props: Arc>>, ) { - if util::is_debug() { - println!( - "Handling {:?} event for container {}", - ev.change, ev.container.id - ); - } match ev.change { ipc::WindowEventType::Focus => { let mut write_lock = win_props.write().unwrap(); @@ -59,13 +53,6 @@ fn handle_window_event( } _ => (), } - - if util::is_debug() { - println!( - "New window properties state:\n{:#?}", - win_props.read().unwrap() - ); - } } fn get_epoch_time_as_millis() -> u128 { diff --git a/src/util.rs b/src/util.rs index 433480b..c0eb924 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,10 +2,6 @@ use crate::window; use std::io::Write; use std::process as proc; -pub fn is_debug() -> bool { - true -} - pub fn get_swayr_socket_path() -> String { let wayland_display = std::env::var("WAYLAND_DISPLAY"); format!( @@ -31,8 +27,11 @@ pub fn swaymsg(args: Vec<&str>) -> String { String::from_utf8(output.stdout).unwrap() } -pub fn select_window<'a>(windows: &'a Vec) -> Option<&'a window::Window<'a>> { - wofi_select("Select window", windows) +pub fn select_window<'a>( + prompt: &'a str, + windows: &'a Vec, +) -> Option<&'a window::Window<'a>> { + wofi_select(prompt, windows) } pub fn wofi_select<'a, 'b, TS>(prompt: &'a str, choices: &'b Vec) -> Option<&'b TS> diff --git a/src/window.rs b/src/window.rs index c959607..41a4dfa 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,9 +1,11 @@ use crate::ipc; +use std::cmp; use std::collections::HashMap; #[derive(Debug)] pub struct Window<'a> { pub node: &'a ipc::Node, + win_props: Option, } impl Window<'_> { @@ -31,6 +33,40 @@ impl Window<'_> { } } +impl PartialEq for Window<'_> { + fn eq(&self, other: &Window) -> bool { + self.get_id() == other.get_id() + } +} + +impl Eq for Window<'_> {} + +impl Ord for Window<'_> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + if self == other { + cmp::Ordering::Equal + } else if self.node.urgent && !other.node.urgent { + cmp::Ordering::Less + } else if !self.node.urgent && other.node.urgent { + std::cmp::Ordering::Greater + } else if self.node.focused && !other.node.focused { + std::cmp::Ordering::Greater + } else if !self.node.focused && other.node.focused { + std::cmp::Ordering::Less + } else { + let lru_a = self.win_props.as_ref().map_or(0, |wp| wp.last_focus_time); + let lru_b = other.win_props.as_ref().map_or(0, |wp| wp.last_focus_time); + lru_a.cmp(&lru_b).reverse() + } + } +} + +impl PartialOrd for Window<'_> { + fn partial_cmp(&self, other: &Window) -> Option { + Some(self.cmp(other)) + } +} + impl<'a> std::fmt::Display for Window<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!( @@ -49,41 +85,20 @@ impl<'a> std::fmt::Display for Window<'a> { } /// Gets all application windows of the tree. -pub fn get_windows(tree: &ipc::Node) -> Vec { +pub fn get_windows( + tree: &ipc::Node, + mut win_props: HashMap, +) -> Vec { let mut v = vec![]; for n in tree.iter() { if n.name.is_some() && (n.r#type == ipc::NodeType::Con || n.r#type == ipc::NodeType::FloatingCon) { - v.push(Window { node: &n }) + v.push(Window { + node: &n, + win_props: win_props.remove(&n.id), + }) } } v } - -/// Sorts windows so that urgent windows come first, the currently focused -/// window comes last, and otherwise windows are sorted in last-recently-used -/// order. -pub fn sort_windows(windows: &mut Vec, win_props: HashMap) { - windows.sort_unstable_by(|a, b| { - if a.node.urgent && !b.node.urgent { - std::cmp::Ordering::Less - } else if !a.node.urgent && b.node.urgent { - std::cmp::Ordering::Greater - } else if a.node.focused && !b.node.focused { - std::cmp::Ordering::Greater - } else if !a.node.focused && b.node.focused { - std::cmp::Ordering::Less - } else { - let lru_a = win_props - .get(&a.node.id) - .map(|p| p.last_focus_time) - .unwrap_or(0); - let lru_b = win_props - .get(&b.node.id) - .map(|p| p.last_focus_time) - .unwrap_or(0); - lru_a.cmp(&lru_b).reverse() - } - }); -}