diff --git a/Cargo.lock b/Cargo.lock index 53ff232..fa7a538 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,9 +270,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "lazy_static" @@ -288,9 +288,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "log" @@ -377,15 +377,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "ppv-lite86" @@ -419,11 +419,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -467,9 +467,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" dependencies = [ "autocfg", "crossbeam-deque", @@ -479,9 +479,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -511,9 +511,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -522,15 +522,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "rt-format" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953eff237fc52cbb1a78d9ef62de48422a42c49c4db65b7e7e9d3aa500b1bdae" +checksum = "45087cee619d316fa4bd1675494acff4a5eaa0892fa53bc364bd246f13e452e2" dependencies = [ "lazy_static", "regex", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "scopeguard" @@ -658,20 +658,20 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04066589568b72ec65f42d65a1a52436e954b168773148893c020269563decf2" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "sysinfo" -version = "0.23.12" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b1e20ee77901236c389ff74618a899ff5fd34719a7ff0fd1d64f0acca5179a" +checksum = "3977ec2e0520829be45c8a2df70db2bf364714d8a748316a10c3c35d4d2b01c9" dependencies = [ "cfg-if", "core-foundation-sys 0.8.3", @@ -743,10 +743,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] -name = "unicode-xid" -version = "0.2.3" +name = "unicode-ident" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "uom" diff --git a/README.md b/README.md index 045cfe3..a62ed76 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,11 @@ program. a "browser" mark to your browser window (using a standard sway `for_window` rule). Then you can provide "browser" as argument to this command to have a convenient browser <-> last-recently-used window toggle. +* `switch-to-matching-or-urgent-or-lru-window` switches to the (first) window + matching the given criterion (see section `CRITERIA` in `sway(5)`) if it + exists and is not already focused. Otherwise, switch to the next urgent + window (if any) or to the last recently used window. + #### Menu switchers diff --git a/swayr/NEWS.md b/swayr/NEWS.md index bf4d2c9..d2ccb35 100644 --- a/swayr/NEWS.md +++ b/swayr/NEWS.md @@ -1,3 +1,12 @@ +swayr v0.19.0 +============= + +- There's a new command `switch-to-matching-or-urgent-or-lru-window` which + switches to the (first) window matching the given criteria (see section + `CRITERIA` in `sway(5)`) if it exists and is not already focused. Otherwise, + switch to the next urgent window (if any) or to the last recently used + window. + swayr v0.18.0 ============= diff --git a/swayr/src/cmds.rs b/swayr/src/cmds.rs index dd88257..3934185 100644 --- a/swayr/src/cmds.rs +++ b/swayr/src/cmds.rs @@ -86,6 +86,11 @@ pub enum SwayrCommand { /// "browser" as argument to this command to have a convenient browser <-> /// last-recently-used window toggle. SwitchToMarkOrUrgentOrLRUWindow { con_mark: String }, + /// Switch to the (first) window matching the given criteria (see section + /// `CRITERIA` in `sway(5)`) if it exists and is not already focused. + /// Otherwise, switch to the next urgent window (if any) or to the last + /// recently used window. + SwitchToMatchingOrUrgentOrLRUWindow { criteria: String }, /// Focus the selected window. SwitchWindow, /// Switch to the selected workspace. @@ -260,10 +265,13 @@ pub fn exec_swayr_cmd(args: ExecSwayrCmdArgs) { switch_to_urgent_or_lru_window(fdata) } SwayrCommand::SwitchToAppOrUrgentOrLRUWindow { name } => { - switch_to_app_or_urgent_or_lru_window(Some(name), fdata) + switch_to_app_or_urgent_or_lru_window(name, fdata) } SwayrCommand::SwitchToMarkOrUrgentOrLRUWindow { con_mark } => { - switch_to_mark_or_urgent_or_lru_window(Some(con_mark), fdata) + switch_to_mark_or_urgent_or_lru_window(con_mark, fdata) + } + SwayrCommand::SwitchToMatchingOrUrgentOrLRUWindow { criteria } => { + switch_to_matching_or_urgent_or_lru_window(criteria, fdata) } SwayrCommand::SwitchWindow => switch_window(fdata), SwayrCommand::SwitchWorkspace => switch_workspace(fdata), @@ -470,35 +478,67 @@ pub fn get_outputs() -> Vec { } pub fn switch_to_urgent_or_lru_window(fdata: &FocusData) { - switch_to_app_or_urgent_or_lru_window(None, fdata) + let root = ipc::get_root_node(false); + let tree = t::get_tree(&root); + let wins = tree.get_windows(fdata); + focus_win_if_not_focused(None, wins.get(0)) } -pub fn switch_to_app_or_urgent_or_lru_window( - name: Option<&str>, - fdata: &FocusData, -) { +pub fn switch_to_app_or_urgent_or_lru_window(name: &str, fdata: &FocusData) { let root = ipc::get_root_node(false); let tree = t::get_tree(&root); let wins = tree.get_windows(fdata); - let app_win = - name.and_then(|n| wins.iter().find(|w| w.node.get_app_name() == n)); + let app_win = wins.iter().find(|w| w.node.get_app_name() == name); focus_win_if_not_focused(app_win, wins.get(0)) } pub fn switch_to_mark_or_urgent_or_lru_window( - con_mark: Option<&str>, + con_mark: &str, fdata: &FocusData, ) { let root = ipc::get_root_node(false); let tree = t::get_tree(&root); let wins = tree.get_windows(fdata); - let marked_win = con_mark.and_then(|mark| { - wins.iter() - .find(|w| w.node.marks.contains(&mark.to_owned())) - }); + let con_mark = &con_mark.to_owned(); + let marked_win = wins.iter().find(|w| w.node.marks.contains(con_mark)); focus_win_if_not_focused(marked_win, wins.get(0)) } +fn switch_to_matching_or_urgent_or_lru_window( + criteria: &str, + fdata: &FocusData, +) { + // TODO: It would be great if sway had some command which given a criteria + // query returns the matching windows. Unfortunately, it doesn't have it + // right now. So we call `CRITERION focus` and check if focus has moved. + // If not, we do the "urgent or LRU" thing. + let root = ipc::get_root_node(false); + let tree = t::get_tree(&root); + let wins = tree.get_windows(fdata); + let prev_win_id = wins + .iter() + .find(|w| w.node.focused) + .map(|w| w.node.id) + .unwrap_or(-1); + run_sway_command(&[criteria, "focus"]); + + // Wait until the focus event had time to arrive. + std::thread::sleep(std::time::Duration::from_millis(50)); + + let root = ipc::get_root_node(false); + let tree = t::get_tree(&root); + let wins = tree.get_windows(fdata); + let cur_win_id = wins + .iter() + .find(|w| w.node.focused) + .map(|w| w.node.id) + .unwrap_or(-1); + + if prev_win_id == cur_win_id { + focus_win_if_not_focused(None, wins.get(0)) + } +} + pub fn focus_win_if_not_focused( win: Option<&t::DisplayNode>, other: Option<&t::DisplayNode>,