From c81854852a3ec65541178de00ae809857a25649d Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Thu, 13 Jul 2023 20:35:57 +0200 Subject: [PATCH 01/45] fix: missing instance in class string There was a case not handled, when a program only provides a class name, not a null terminated list with [instance\0class]. In the case where there isn't an instance string i3wsr would fail to render anything, even though the program technically provides one. I had to rewrite how the string returned from window is parsed, instead of going through a mutable iterator instead collect to a vector, and then slice and pattern match to locate the correct values Error handling isn't perfect, it'll report a missing class if the result vector doesn't match any paths, instance shouldn't ever report if I see things correctly. I do plan on refactoring error handling later so this'll do. --- src/lib.rs | 59 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 38c1dad..104a868 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ use i3ipc::{ #[macro_use] extern crate failure_derive; extern crate failure; -use failure::Error; +use failure::{ Error, err_msg }; extern crate serde; @@ -99,29 +99,43 @@ fn get_title( }; let reply = get_property(&conn, id, xproto::ATOM_WM_CLASS)?; - let mut reply = reply.split('\0'); - - // Store vm_instance, leaving only class in results - let wm_instance = reply - .next() - .ok_or_else(|| LookupError::WindowInstance(id))?; + let result: Vec<&str> = reply.split('\0').collect(); // Store wm_class - let wm_class = reply.next().ok_or_else(|| LookupError::WindowClass(id))?; + // use pattern matching for vector slice to extract class depending on position + let wm_class = match result[..] { + [class] => class, + [_, class] => class, + [_, class, ..] => class, + _ => { + return Err(err_msg(LookupError::WindowClass(id))) + } + }; + + // Store vm_instance, default to class if non is present + let wm_instance = match result[..] { + [class] => class, + [instance, _] => instance, + [instance, _, ..] => instance, + _ => { + return Err(err_msg(LookupError::WindowInstance(id))) + } + }; + + // Store window name, fall back to class + let wm_name = { + let name = get_property(&conn, id, xproto::ATOM_WM_NAME)?; + if name.is_empty() { + wm_class.to_string() + } else { + name + } + }; // Set target from options let target = match use_prop { - "class" => wm_class.to_string(), "instance" => wm_instance.to_string(), - "name" => { - let name = get_property(&conn, id, xproto::ATOM_WM_NAME)?; - if name.is_empty() { - wm_class.to_string() - } else { - name - } - - }, + "name" => wm_name, _ => wm_class.to_string() }; @@ -136,18 +150,11 @@ fn get_title( } }; - // either use icon for wm_instance, or fall back to icon for class - let key = if config.icons.contains_key(title) { - title - } else { - wm_class - }; - let no_names = get_option(&config, "no_names"); let no_icon_names = get_option(&config, "no_icon_names"); // Format final result - Ok(match config.icons.get(key) { + Ok(match config.icons.get(title) { Some(icon) => { if no_icon_names || no_names { format!("{}", icon) From 7379b3dea9e2abf0e76ff7a5de3a13c2b5f2a345 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Thu, 13 Jul 2023 21:02:25 +0200 Subject: [PATCH 02/45] test: update ubuntu version --- Vagrantfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Vagrantfile b/Vagrantfile index 0c9e20c..6c72d61 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -2,7 +2,7 @@ VAGRANTFILE_API_VERSION = '2' Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.define :ubuntu do |ubuntu| - ubuntu.vm.box = 'ubuntu/xenial64' + ubuntu.vm.box = 'ubuntu/lunar64' ubuntu.vm.provision 'shell', path: 'script/vagrant_root.sh' ubuntu.vm.provision 'shell', privileged: false, path: 'script/vagrant_user.sh' end From 7b9017e3ee3f60d98f9013e544470ad12b4d39d5 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Thu, 13 Jul 2023 21:08:09 +0200 Subject: [PATCH 03/45] doc: update instance explanation --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 14a0e56..b9f8e4a 100644 --- a/README.md +++ b/README.md @@ -172,9 +172,9 @@ This is the default, and the most succinct. #### Instance -Use WM_INSTANCE instead of WM_CLASS when assigning workspace names, instance is -usually more specific. i3wsr will try to match icon with instance, and if that -fail, will fall back to class. +Use WM\_INSTANCE instead of WM\_CLASS when assigning workspace names, instance +is usually more specific. i3wsr will try to get the instance but if it isn't +defined will use class instead. A use case for this option could be launching `chromium --app="https://web.whatsapp.com"`, and then assign a different icon to whatsapp From 52c18f24a82593883b68531c3ba1105fcc0e7679 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Thu, 13 Jul 2023 22:29:39 +0200 Subject: [PATCH 04/45] deps: update to latest version of xcb Fix issues that arose with new namespaces and etc --- Cargo.lock | 249 +++++++++++++++++++++++++++++++++++------------------ Cargo.toml | 2 +- src/lib.rs | 47 +++------- 3 files changed, 178 insertions(+), 120 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 881098c..0adf26c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,27 +4,33 @@ version = 3 [[package]] name = "addr2line" -version = "0.12.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49806b9dadc843c61e7c97e72490ad7f7220ae249012fbda9ad0609457c0543" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" dependencies = [ "gimli", ] +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "0.7.15" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] @@ -42,34 +48,36 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.48" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" dependencies = [ "addr2line", - "cfg-if 0.1.10", + "cc", + "cfg-if", "libc", + "miniz_oxide", "object", "rustc-demangle", ] [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "cfg-if" -version = "0.1.10" +name = "cc" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -79,9 +87,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.1" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -103,9 +111,9 @@ dependencies = [ [[package]] name = "dirs-sys" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -114,9 +122,9 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "encoding" @@ -209,32 +217,32 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "synstructure", ] [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "libc", "wasi", ] [[package]] name = "gimli" -version = "0.21.0" +version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" [[package]] name = "hermit-abi" -version = "0.1.13" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -281,9 +289,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.5" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "lazy_static" @@ -293,73 +301,104 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.71" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.8" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" -dependencies = [ - "cfg-if 0.1.10", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" -version = "2.3.4" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "object" -version = "0.19.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" dependencies = [ - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +dependencies = [ + "memchr", ] [[package]] name = "quote" -version = "1.0.6" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.11" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", "redox_syscall", + "thiserror", ] [[package]] name = "regex" -version = "1.4.5" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -368,47 +407,47 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.23" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "rustc-demangle" -version = "0.1.16" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" [[package]] name = "serde" -version = "1.0.111" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.111" +version = "1.0.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.25", ] [[package]] name = "serde_json" -version = "1.0.53" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" +checksum = "b5062a995d481b2308b6064e9af76011f2921c35f97b0468811ed9f6cd91dfed" dependencies = [ "itoa", "ryu", @@ -423,24 +462,35 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.30" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "synstructure" -version = "0.12.3" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "unicode-xid", ] @@ -453,26 +503,52 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + [[package]] name = "toml" -version = "0.5.6" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + [[package]] name = "unicode-width" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "vec_map" @@ -482,15 +558,15 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -510,10 +586,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "xcb" -version = "0.9.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6" +checksum = "4b90c622d513012e7419594a2138953603c63848cb189041e7b5dc04d3895da5" dependencies = [ + "bitflags", "libc", - "log", + "quick-xml", ] diff --git a/Cargo.toml b/Cargo.toml index 6a808ac..a365fc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["TODOs.org*", "/script", "/assets/*", "Vagrantfile"] travis-ci = { repository = "roosta/i3wsr" } [dependencies] -xcb = "0.9" +xcb = "1.2.1" exitfailure = "0.5.1" failure = "0.1.8" failure_derive = "0.1.8" diff --git a/src/lib.rs b/src/lib.rs index 104a868..2cf6d55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ extern crate xcb; -use xcb::xproto; +use xcb::{x, XidNew}; + extern crate itertools; use itertools::Itertools; @@ -59,21 +60,20 @@ fn get_option(config: &Config, key: &str) -> bool { fn get_property( conn: &xcb::Connection, id: u32, - prop: xproto::Atom, + property: x::Atom, ) -> Result { - let window: xproto::Window = id; - let cookie = xproto::get_property( - &conn, - false, + let window = unsafe{ XidNew::new(id) }; + let cookie = conn.send_request(&x::GetProperty { + delete: false, window, - prop, - xproto::ATOM_STRING, - 0, - 1024, - ); + property, + r#type: x::ATOM_STRING, + long_offset: 0, + long_length: 1024, + }); - let reply = cookie.get_reply()?; + let reply = conn.wait_for_reply(cookie)?; if let Ok(s) = std::str::from_utf8(reply.value()) { Ok(s.to_string()) } else { @@ -98,7 +98,7 @@ fn get_title( None => "class", }; - let reply = get_property(&conn, id, xproto::ATOM_WM_CLASS)?; + let reply = get_property(&conn, id, x::ATOM_WM_CLASS)?; let result: Vec<&str> = reply.split('\0').collect(); // Store wm_class @@ -124,7 +124,7 @@ fn get_title( // Store window name, fall back to class let wm_name = { - let name = get_property(&conn, id, xproto::ATOM_WM_NAME)?; + let name = get_property(&conn, id, x::ATOM_WM_NAME)?; if name.is_empty() { wm_class.to_string() } else { @@ -181,25 +181,6 @@ fn get_title( }) } -/// Checks if window is of type normal. The problem with this is that not all -/// windows define a type (spotify, I'm looking at you) Also, even if the window -/// type is normal, the class returned will be the same regardless of type, and -/// it won't trigger a change. We do end up doing some redundant calculations by -/// not using this but makes the program much more forgiving. -fn _is_normal(conn: &xcb::Connection, id: u32) -> Result { - let window: xproto::Window = id; - let ident = xcb::intern_atom(&conn, true, "_NET_WM_WINDOW_TYPE") - .get_reply()? - .atom(); - let reply = xproto::get_property(&conn, false, window, ident, xproto::ATOM_ATOM, 0, 1024) - .get_reply()?; - let actual: u32 = reply.value()[0]; - let expected: u32 = xcb::intern_atom(&conn, true, "_NET_WM_WINDOW_TYPE_NORMAL") - .get_reply()? - .atom(); - Ok(actual == expected) -} - /// return a collection of workspace nodes fn get_workspaces(tree: Node) -> Vec { let mut out = Vec::new(); From e4160f501cf57f012defc5574466e3978953e948 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 12:23:31 +0200 Subject: [PATCH 05/45] refactor: rewrite failure logic Previous implementation was to complicated IMO, and uses deprecated packages. I reference to the rust book on how to idiomatically propagate errors, without extra dependencies Removed exitfailure because rust handles a result return type how i'd expect now --- Cargo.lock | 210 ++++++++++++++++++-------------------------------- Cargo.toml | 5 +- src/config.rs | 7 +- src/lib.rs | 81 ++++++++++--------- src/main.rs | 35 +++++---- src/regex.rs | 6 +- 6 files changed, 146 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0adf26c..a98d45a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,21 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "aho-corasick" version = "1.0.2" @@ -46,21 +31,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -73,12 +43,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - [[package]] name = "cfg-if" version = "1.0.0" @@ -102,22 +66,23 @@ dependencies = [ [[package]] name = "dirs" -version = "4.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.3.7" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", + "option-ext", "redox_users", - "winapi", + "windows-sys", ] [[package]] @@ -190,37 +155,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" -[[package]] -name = "exitfailure" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff5bd832af37f366c6c194d813a11cd90ac484f124f079294f28e357ae40515" -dependencies = [ - "failure", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -232,12 +166,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -266,9 +194,6 @@ dependencies = [ "clap", "dirs", "encoding", - "exitfailure", - "failure", - "failure_derive", "i3ipc", "itertools", "lazy_static", @@ -318,22 +243,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "object" -version = "0.31.1" +name = "option-ext" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "proc-macro2" @@ -411,12 +324,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - [[package]] name = "ryu" version = "1.0.14" @@ -440,7 +347,7 @@ checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn", ] [[package]] @@ -460,17 +367,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.25" @@ -482,18 +378,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -520,7 +404,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.25", + "syn", ] [[package]] @@ -544,12 +428,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - [[package]] name = "vec_map" version = "0.8.2" @@ -584,6 +462,72 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "xcb" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index a365fc2..fba453a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,6 @@ travis-ci = { repository = "roosta/i3wsr" } [dependencies] xcb = "1.2.1" -exitfailure = "0.5.1" -failure = "0.1.8" -failure_derive = "0.1.8" lazy_static = "1.4.0" clap = "2.33.1" toml = "0.5.6" @@ -27,7 +24,7 @@ serde = { version = "1.0.111", features = ["derive"] } itertools = "0.9.0" regex = "1" encoding = "0.2" -dirs = "4.0.0" +dirs = "5.0.1" [dependencies.i3ipc] version = "0.10.1" diff --git a/src/config.rs b/src/config.rs index 6fa3d88..c6e65df 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,3 @@ -use failure::Error; use serde::Deserialize; use std::collections::HashMap as Map; use std::fs::File; @@ -10,6 +9,8 @@ lazy_static! { pub static ref EMPTY_OPT_MAP: Map = Map::new(); } +use std::error::Error; + #[derive(Deserialize)] #[serde(default)] pub struct Config { @@ -20,7 +21,7 @@ pub struct Config { } impl Config { - pub fn new(filename: &Path, icons_override: &str) -> Result { + pub fn new(filename: &Path, icons_override: &str) -> Result> { let file_config = read_toml_config(filename)?; Ok(Config { icons: file_config @@ -44,7 +45,7 @@ impl Default for Config { } } -fn read_toml_config(filename: &Path) -> Result { +fn read_toml_config(filename: &Path) -> Result> { let mut file = File::open(filename)?; let mut buffer = String::new(); file.read_to_string(&mut buffer)?; diff --git a/src/lib.rs b/src/lib.rs index 2cf6d55..dc47836 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,10 +16,10 @@ use i3ipc::{ I3Connection, }; -#[macro_use] -extern crate failure_derive; -extern crate failure; -use failure::{ Error, err_msg }; +// #[macro_use] +// extern crate failure_derive; +// extern crate failure; +// use failure::{ Error, err_msg }; extern crate serde; @@ -36,17 +36,22 @@ pub mod config; pub mod icons; pub mod regex; +use std::error::Error; + use config::Config; -#[derive(Debug, Fail)] -enum LookupError { - #[fail(display = "Failed to get a class for window id: {}", _0)] - WindowClass(u32), - #[fail(display = "Failed to get a instance for window id: {}", _0)] - WindowInstance(u32), - #[fail(display = "Failed to get title for workspace: {:#?}", _0)] - WorkspaceTitle(Box), -} +#[derive(Debug)] +struct MyError(String); + +// #[derive(Debug)] +// enum LookupError { +// #[fail(display = "Failed to get a class for window id: {}", _0)] +// WindowClass(u32), +// #[fail(display = "Failed to get a instance for window id: {}", _0)] +// WindowInstance(u32), +// #[fail(display = "Failed to get title for workspace: {:#?}", _0)] +// WorkspaceTitle(Box), +// } /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { @@ -61,7 +66,7 @@ fn get_property( conn: &xcb::Connection, id: u32, property: x::Atom, -) -> Result { +) -> Result> { let window = unsafe{ XidNew::new(id) }; let cookie = conn.send_request(&x::GetProperty { @@ -91,7 +96,7 @@ fn get_title( id: u32, config: &Config, res: &Vec, -) -> Result { +) -> Result> { let use_prop = match config.general.get("wm_property") { Some(prop) => prop, @@ -103,22 +108,12 @@ fn get_title( // Store wm_class // use pattern matching for vector slice to extract class depending on position - let wm_class = match result[..] { - [class] => class, - [_, class] => class, - [_, class, ..] => class, + let [ wm_class, wm_instance ] = match result[..] { + [class] => [ class, "" ], + [instance, class] => [ class, instance ], + [instance, class, ..] => [ class, instance ], _ => { - return Err(err_msg(LookupError::WindowClass(id))) - } - }; - - // Store vm_instance, default to class if non is present - let wm_instance = match result[..] { - [class] => class, - [instance, _] => instance, - [instance, _, ..] => instance, - _ => { - return Err(err_msg(LookupError::WindowInstance(id))) + Err(format!("Failed to get a instance for window id: {}", id))? } }; @@ -134,7 +129,14 @@ fn get_title( // Set target from options let target = match use_prop { - "instance" => wm_instance.to_string(), + "instance" => { + if wm_instance.is_empty() { + wm_class.to_string() + } else { + wm_instance.to_string() + } + + }, "name" => wm_name, _ => wm_class.to_string() }; @@ -250,7 +252,7 @@ pub fn update_tree( i3_conn: &mut I3Connection, config: &Config, res: &Vec, -) -> Result<(), Error> { +) -> Result<(), Box> { let tree = i3_conn.get_tree()?; for workspace in get_workspaces(tree) { let separator = match config.general.get("separator") { @@ -275,11 +277,14 @@ pub fn update_tree( } else { titles }; - - let old: String = workspace - .name + let old: String = workspace.name .to_owned() - .ok_or_else(|| LookupError::WorkspaceTitle(Box::new(workspace)))?; + .ok_or_else(|| { + format!( + "Failed to get workspace name for workspace: {:#?}", + workspace + ) + })?; let mut new = old.split(' ').next().unwrap().to_owned(); @@ -302,7 +307,7 @@ pub fn handle_window_event( i3_conn: &mut I3Connection, config: &Config, res: &Vec, -) -> Result<(), Error> { +) -> Result<(), Box> { match e.change { WindowChange::New | WindowChange::Close | WindowChange::Move | WindowChange::Title => { update_tree(x_conn, i3_conn, config, res)?; @@ -319,7 +324,7 @@ pub fn handle_ws_event( i3_conn: &mut I3Connection, config: &Config, res: &Vec, -) -> Result<(), Error> { +) -> Result<(), Box> { match e.change { WorkspaceChange::Empty | WorkspaceChange::Focus => { update_tree(x_conn, i3_conn, config, res)?; diff --git a/src/main.rs b/src/main.rs index 36b3b0b..26e23c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,23 +2,20 @@ extern crate i3ipc; use std::{path::Path}; use dirs::config_dir; -use failure::ResultExt; use i3ipc::{event::Event, I3Connection, I3EventListener, Subscription}; extern crate xcb; extern crate i3wsr; -extern crate exitfailure; -use exitfailure::ExitFailure; - #[macro_use] extern crate clap; use clap::{App, Arg}; +use std::error::Error; use i3wsr::config::{Config}; -fn main() -> Result<(), ExitFailure> { +fn main() -> Result<(), Box> { let matches = App::new("i3wsr - i3 workspace renamer") .version(crate_version!()) .author("Daniel Berg ") @@ -65,30 +62,34 @@ fn main() -> Result<(), ExitFailure> { ) .get_matches(); + // Parse cmd args let icons = matches.value_of("icons").unwrap_or(""); let no_icon_names = matches.is_present("no-icon-names"); let no_names = matches.is_present("no-names"); let remove_duplicates = matches.is_present("remove-duplicates"); let wm_property = matches.is_present("wm-property"); - let mut default_config = config_dir().unwrap(); - default_config.push("i3wsr/config.toml"); - let mut config_path_used: Option<&Path> = None; - let mut config = match matches.value_of("config") { + let default_config = config_dir().unwrap().join("i3wsr/config.toml"); + + // handle config + let config_result = match matches.value_of("config") { Some(filename) => { - let config_file = Path::new(filename); - config_path_used = Some(config_file); - Config::new(config_file, icons) + Config::new(Path::new(filename), icons) }, None => { - if (&default_config).exists() { - config_path_used = Some(&default_config); + if (default_config).exists() { Config::new(&default_config, icons) } else { - Ok(Config {icons: i3wsr::icons::get_icons(icons), ..Default::default()}) + Ok(Config { + icons: i3wsr::icons::get_icons(icons), + ..Default::default() + }) } } - }.with_context(|_|format!("Could not parse config file:\n {:?}", config_path_used.unwrap()))?; - + }; + let mut config = match config_result { + Ok(c) => c, + Err(e) => panic!("Error with config file: {}", e) + }; if no_icon_names { config.options.insert("no_icon_names".to_string(), no_icon_names); } diff --git a/src/regex.rs b/src/regex.rs index 657df4a..d62d1f8 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -1,14 +1,14 @@ -use failure::Error; use libre::Regex; use crate::Config; +use std::error::Error; pub type Point = (Regex, String); -fn compile((k, v): (&String, &String)) -> Result { +fn compile((k, v): (&String, &String)) -> Result> { let re = Regex::new(&format!(r"{}", k))?; Ok((re, v.to_owned())) } -pub fn parse_config(config: &Config) -> Result, Error> { +pub fn parse_config(config: &Config) -> Result, Box> { config.aliases.iter().map(compile).collect() } From e9707d970e3691654f9f844e51b357ab41a3eef4 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 12:37:38 +0200 Subject: [PATCH 06/45] test: fix tests after failure refactor --- src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dc47836..2f789e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -336,12 +336,12 @@ pub fn handle_ws_event( #[cfg(test)] mod tests { - use failure::Error; use i3ipc::reply::NodeType; use std::env; + use std::error::Error; #[test] - fn connection_tree() -> Result<(), Error> { + fn connection_tree() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); let (x_conn, _) = super::xcb::Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; @@ -365,7 +365,7 @@ mod tests { } #[test] - fn get_title() -> Result<(), Error> { + fn get_title() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); let (x_conn, _) = super::xcb::Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; @@ -397,7 +397,7 @@ mod tests { } #[test] - fn collect_titles() -> Result<(), Error> { + fn collect_titles() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); let (x_conn, _) = super::xcb::Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; @@ -415,7 +415,7 @@ mod tests { } #[test] - fn get_ids() -> Result<(), Error> { + fn get_ids() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); let mut i3_conn = super::I3Connection::connect()?; let tree = i3_conn.get_tree()?; From 0c12dd06d525217c13ecc1b35abb7049fd50cbbc Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 12:40:27 +0200 Subject: [PATCH 07/45] feat: update casing etc for error msg --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2f789e7..e45aca5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,7 @@ fn get_title( [instance, class] => [ class, instance ], [instance, class, ..] => [ class, instance ], _ => { - Err(format!("Failed to get a instance for window id: {}", id))? + Err(format!("failed to get a instance for window id {}", id))? } }; From 9fbfc98d2cfff50c5c4f8287a82cf1692440d924 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 12:41:20 +0200 Subject: [PATCH 08/45] comments cleanup --- src/lib.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e45aca5..0396216 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,11 +16,6 @@ use i3ipc::{ I3Connection, }; -// #[macro_use] -// extern crate failure_derive; -// extern crate failure; -// use failure::{ Error, err_msg }; - extern crate serde; #[macro_use] @@ -43,16 +38,6 @@ use config::Config; #[derive(Debug)] struct MyError(String); -// #[derive(Debug)] -// enum LookupError { -// #[fail(display = "Failed to get a class for window id: {}", _0)] -// WindowClass(u32), -// #[fail(display = "Failed to get a instance for window id: {}", _0)] -// WindowInstance(u32), -// #[fail(display = "Failed to get title for workspace: {:#?}", _0)] -// WorkspaceTitle(Box), -// } - /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { return match config.options.get(key) { From 94490eba88d920d7857d2dd5550cec2e11916ad0 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 16:53:23 +0200 Subject: [PATCH 09/45] refactor: remove lazy_static Really can't se a need for this, the advantages for lazy static isn't really in effect here, we might as well just compile everything --- Cargo.lock | 7 ------- Cargo.toml | 1 - src/config.rs | 23 +++++++++-------------- src/icons.rs | 45 ++++++++++++++------------------------------- src/lib.rs | 3 --- 5 files changed, 23 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a98d45a..6a545c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,7 +196,6 @@ dependencies = [ "encoding", "i3ipc", "itertools", - "lazy_static", "regex", "serde", "toml", @@ -218,12 +217,6 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.147" diff --git a/Cargo.toml b/Cargo.toml index fba453a..9db202e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ travis-ci = { repository = "roosta/i3wsr" } [dependencies] xcb = "1.2.1" -lazy_static = "1.4.0" clap = "2.33.1" toml = "0.5.6" serde = { version = "1.0.111", features = ["derive"] } diff --git a/src/config.rs b/src/config.rs index c6e65df..676503c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,23 +1,18 @@ use serde::Deserialize; -use std::collections::HashMap as Map; +use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::path::Path; -lazy_static! { - pub static ref EMPTY_MAP: Map = Map::new(); - pub static ref EMPTY_OPT_MAP: Map = Map::new(); -} - use std::error::Error; #[derive(Deserialize)] #[serde(default)] pub struct Config { - pub icons: Map, - pub aliases: Map, - pub general: Map, - pub options: Map, + pub icons: HashMap, + pub aliases: HashMap, + pub general: HashMap, + pub options: HashMap, } impl Config { @@ -37,10 +32,10 @@ impl Config { impl Default for Config { fn default() -> Self { Config { - icons: super::icons::NONE.clone(), - aliases: EMPTY_MAP.clone(), - general: EMPTY_MAP.clone(), - options: EMPTY_OPT_MAP.clone(), + icons: HashMap::new(), + aliases: HashMap::new(), + general: HashMap::new(), + options: HashMap::new(), } } } diff --git a/src/icons.rs b/src/icons.rs index 7440c67..32c47d9 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -1,36 +1,19 @@ -use std::collections::HashMap as Map; +use std::collections::HashMap; use std::char; -// taken from https://github.com/greshake/i3status-rust/blob/master/src/icons.rs -macro_rules! map_to_owned ( - { $($key:expr => $value:expr),+ } => { - { - let mut m = ::std::collections::HashMap::new(); - $( - m.insert($key.to_owned(), $value.to_owned()); - )+ - m - } - }; -); - -lazy_static! { - pub static ref AWESOME: Map = map_to_owned! { - "Firefox" => '', - "TelegramDesktop" => '', - "Alacritty" => '', - "Thunderbird" => '', - "KeeWeb" => '', - "Org.gnome.Nautilus" => '', - "Evince" => '' - }; - - pub static ref NONE: Map = Map::new(); -} - -pub fn get_icons(name: &str) -> Map { +pub fn get_icons(name: &str) -> HashMap { match name { - "awesome" => AWESOME.clone(), - _ => NONE.clone(), + "awesome" => { + return HashMap::from([ + ("Firefox".to_string(), ''), + ("TelegramDesktop".to_string(), ''), + ("Alacritty".to_string(), ''), + ("Thunderbird".to_string(), ''), + ("KeeWeb".to_string(), ''), + ("Org.gnome.Nautilus".to_string(), ''), + ("Evince".to_string(), '') + ]); + }, + _ => HashMap::new(), } } diff --git a/src/lib.rs b/src/lib.rs index 0396216..44f4b70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,9 +18,6 @@ use i3ipc::{ extern crate serde; -#[macro_use] -extern crate lazy_static; - extern crate toml; extern crate encoding; From a2c962f66e977c1e53bbb91332e0af39f563690a Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 17:02:08 +0200 Subject: [PATCH 10/45] refactor: move i3ipc to dependency section --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9db202e..3a333b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,5 @@ itertools = "0.9.0" regex = "1" encoding = "0.2" dirs = "5.0.1" +i3ipc = "0.10.1" -[dependencies.i3ipc] -version = "0.10.1" From 85fbdbb7f257e68f54af86f359c9efb00ab56e6f Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 17:04:43 +0200 Subject: [PATCH 11/45] refactor: remove unneeded extern declarations Remove regex alias --- src/lib.rs | 19 +------------------ src/regex.rs | 2 +- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 44f4b70..d19f3f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,4 @@ -extern crate xcb; use xcb::{x, XidNew}; - -extern crate itertools; -use itertools::Itertools; - -extern crate regex as libre; - -extern crate i3ipc; use i3ipc::{ event::{ inner::{WindowChange, WorkspaceChange}, @@ -15,26 +7,17 @@ use i3ipc::{ reply::{Node, NodeType}, I3Connection, }; - -extern crate serde; - -extern crate toml; - -extern crate encoding; use encoding::{Encoding, DecoderTrap}; use encoding::all::ISO_8859_1; +use itertools::Itertools; pub mod config; pub mod icons; pub mod regex; use std::error::Error; - use config::Config; -#[derive(Debug)] -struct MyError(String); - /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { return match config.options.get(key) { diff --git a/src/regex.rs b/src/regex.rs index d62d1f8..84524f0 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -1,4 +1,4 @@ -use libre::Regex; +use regex::Regex; use crate::Config; use std::error::Error; From d24a3ddb776e716575a64d12e133876856f4bbdc Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:02:37 +0200 Subject: [PATCH 12/45] refactor: update clap, rewrite args parsing Was lagging very behind here, so I had to rewrite most of the argument parsing, while keeping the lib api the same. Improvements could be made. --- Cargo.lock | 230 +++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 2 +- src/main.rs | 166 ++++++++++++++++++++----------------- 3 files changed, 254 insertions(+), 144 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6a545c2..9bb7ab8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,23 +12,52 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "anstream" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ - "winapi", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", ] [[package]] -name = "atty" -version = "0.2.14" +name = "anstyle" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anstyle-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", ] [[package]] @@ -37,12 +66,24 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -51,19 +92,51 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.34.0" +version = "4.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" dependencies = [ - "ansi_term", - "atty", - "bitflags", + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", "strsim", - "textwrap", - "unicode-width", - "vec_map", ] +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "dirs" version = "5.0.1" @@ -155,6 +228,27 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "getrandom" version = "0.2.10" @@ -166,14 +260,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "i3ipc" @@ -202,6 +299,17 @@ dependencies = [ "xcb", ] +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + [[package]] name = "itertools" version = "0.9.0" @@ -223,6 +331,12 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + [[package]] name = "log" version = "0.4.19" @@ -235,6 +349,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + [[package]] name = "option-ext" version = "0.2.0" @@ -274,7 +394,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -317,6 +437,19 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +[[package]] +name = "rustix" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.14" @@ -356,9 +489,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" @@ -371,15 +504,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.43" @@ -416,16 +540,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" [[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "vec_map" -version = "0.8.2" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "wasi" @@ -433,28 +551,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -527,7 +623,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b90c622d513012e7419594a2138953603c63848cb189041e7b5dc04d3895da5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", "quick-xml", ] diff --git a/Cargo.toml b/Cargo.toml index 3a333b9..fd3df22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ travis-ci = { repository = "roosta/i3wsr" } [dependencies] xcb = "1.2.1" -clap = "2.33.1" +clap = { version = "4.3.11", features = ["derive"] } toml = "0.5.6" serde = { version = "1.0.111", features = ["derive"] } itertools = "0.9.0" diff --git a/src/main.rs b/src/main.rs index 26e23c9..f37c331 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,83 +1,82 @@ -extern crate i3ipc; use std::{path::Path}; - use dirs::config_dir; -use i3ipc::{event::Event, I3Connection, I3EventListener, Subscription}; +use i3ipc::{ + event::Event, + I3Connection, + I3EventListener, + Subscription +}; +use std::error::Error; +use i3wsr::config::{Config}; +use clap::{Parser, ValueEnum}; -extern crate xcb; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] +enum Icons { + Awesome, +} -extern crate i3wsr; +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] +enum Properties { + Class, + Instance, + Name +} -#[macro_use] -extern crate clap; -use clap::{App, Arg}; -use std::error::Error; +/// i3wsr config +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { -use i3wsr::config::{Config}; + /// Path to toml config file + #[arg(short, long)] + config: Option, + + /// Sets icons to be used + #[arg(short, long)] + icons: Option, + + /// Display only icon (if available) otherwise display name + #[arg(short = 'm', long)] + no_icon_names: bool, + + /// Do not display names + #[arg(short, long)] + no_names: bool, + + /// Remove duplicate entries in workspace + #[arg(short, long)] + remove_duplicates: bool, + + /// Which window property to use + #[arg(short = 'p', long)] + wm_property: Option + +} fn main() -> Result<(), Box> { - let matches = App::new("i3wsr - i3 workspace renamer") - .version(crate_version!()) - .author("Daniel Berg ") - .arg( - Arg::with_name("icons") - .long("icons") - .short("i") - .help("Sets icons to be used") - .possible_values(&["awesome"]) - .takes_value(true) - ) - .arg( - Arg::with_name("no-icon-names") - .long("no-icon-names") - .short("m") - .help("Display only icon (if available) otherwise display name"), - ) - .arg( - Arg::with_name("no-names") - .long("no-names") - .short("n") - .help("Do not display names") - ) - .arg( - Arg::with_name("config") - .long("config") - .short("c") - .help("Path to toml config file") - .takes_value(true) - ) - .arg( - Arg::with_name("remove-duplicates") - .long("remove-duplicates") - .short("r") - .help("Remove duplicate entries in workspace") - ) - .arg( - Arg::with_name("wm-property") - .long("wm-property") - .short("p") - .help("Which window property to use when matching alias, icons") - .possible_values(&["class", "instance", "name"]) - .takes_value(true) - ) - .get_matches(); - - // Parse cmd args - let icons = matches.value_of("icons").unwrap_or(""); - let no_icon_names = matches.is_present("no-icon-names"); - let no_names = matches.is_present("no-names"); - let remove_duplicates = matches.is_present("remove-duplicates"); - let wm_property = matches.is_present("wm-property"); - let default_config = config_dir().unwrap().join("i3wsr/config.toml"); + let args = Args::parse(); + + // icons + // Not really that useful this opt but keeping for posterity + let icons = match args.icons { + Some(icons) => { + match icons { + Icons::Awesome => "awesome", + } + }, + None => "" + }; // handle config - let config_result = match matches.value_of("config") { + let xdg_config = config_dir().unwrap().join("i3wsr/config.toml"); + let config_result = match args.config.as_deref() { Some(filename) => { + println!("{filename}"); Config::new(Path::new(filename), icons) }, None => { - if (default_config).exists() { - Config::new(&default_config, icons) + if (xdg_config).exists() { + Config::new(&xdg_config, icons) } else { Ok(Config { icons: i3wsr::icons::get_icons(icons), @@ -86,24 +85,39 @@ fn main() -> Result<(), Box> { } } }; + let mut config = match config_result { Ok(c) => c, Err(e) => panic!("Error with config file: {}", e) }; - if no_icon_names { - config.options.insert("no_icon_names".to_string(), no_icon_names); - } - if no_names { - config.options.insert("no_names".to_string(), no_names); + + + // Flags + if args.no_icon_names { + config.options.insert("no_icon_names".to_string(), args.no_icon_names); } - if remove_duplicates { - config.options.insert("remove_duplicates".to_string(), remove_duplicates); + + if args.no_names { + config.options.insert("no_names".to_string(), args.no_names); } - if wm_property { - let v = matches.value_of("wm-property").unwrap_or("class"); - config.general.insert("wm_property".to_string(), v.to_string()); + + if args.remove_duplicates { + config.options.insert("remove_duplicates".to_string(), args.remove_duplicates); } + // wm property + let wm_property = match args.wm_property { + Some(prop) => { + match prop { + Properties::Class => String::from("class"), + Properties::Instance => String::from("instance"), + Properties::Name => String::from("name") + } + }, + None => String::from("class") + }; + config.general.insert("wm_property".to_string(), wm_property); + let res = i3wsr::regex::parse_config(&config)?; let mut listener = I3EventListener::connect()?; let subs = [Subscription::Window, Subscription::Workspace]; From 93b27fa87e096bc54fe755e0d730e213f4eca9a6 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:21:19 +0200 Subject: [PATCH 13/45] refactor: move cmd arg parsing to new setup fn --- src/main.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index f37c331..2b70617 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,7 +53,9 @@ struct Args { } -fn main() -> Result<(), Box> { +/// Setup program by handling args and populating config +/// Returns result containing config +fn setup() -> Result> { let args = Args::parse(); // icons @@ -86,11 +88,7 @@ fn main() -> Result<(), Box> { } }; - let mut config = match config_result { - Ok(c) => c, - Err(e) => panic!("Error with config file: {}", e) - }; - + let mut config = config_result?; // Flags if args.no_icon_names { @@ -117,7 +115,13 @@ fn main() -> Result<(), Box> { None => String::from("class") }; config.general.insert("wm_property".to_string(), wm_property); + Ok(config) +} +/// Entry main loop: continusly listen to i3 window events and workspace events, or exit on +/// abnormal error. +fn main() -> Result<(), Box> { + let config = setup()?; let res = i3wsr::regex::parse_config(&config)?; let mut listener = I3EventListener::connect()?; let subs = [Subscription::Window, Subscription::Workspace]; @@ -142,6 +146,5 @@ fn main() -> Result<(), Box> { _ => {} } } - Ok(()) } From 685f42fb69956c2a6acf31b12a0f2a34d0d8432c Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:23:32 +0200 Subject: [PATCH 14/45] deps: update toml (0.7.6) --- Cargo.lock | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 +- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bb7ab8..9cae282 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,6 +228,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -260,6 +266,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -299,6 +311,16 @@ dependencies = [ "xcb", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -487,6 +509,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.10.0" @@ -526,11 +557,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f8751d9c1b03c6500c387e96f81f815a4f8e72d142d2d4a9ffa6fedd51ddee7" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -617,6 +673,15 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winnow" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" +dependencies = [ + "memchr", +] + [[package]] name = "xcb" version = "1.2.1" diff --git a/Cargo.toml b/Cargo.toml index fd3df22..5994487 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ travis-ci = { repository = "roosta/i3wsr" } [dependencies] xcb = "1.2.1" clap = { version = "4.3.11", features = ["derive"] } -toml = "0.5.6" +toml = "0.7.6" serde = { version = "1.0.111", features = ["derive"] } itertools = "0.9.0" regex = "1" From e0ec2c12c1ce22b6d30cc314934c181da821d15f Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:24:10 +0200 Subject: [PATCH 15/45] fix: remove old file from package exclude --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5994487..4c55e55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ readme = "README.md" keywords = ["i3-wm", "window-manager", "workspaces", "linux"] categories = ["command-line-utilities"] license = "MIT" -exclude = ["TODOs.org*", "/script", "/assets/*", "Vagrantfile"] +exclude = ["/script", "/assets/*", "Vagrantfile"] [badges] travis-ci = { repository = "roosta/i3wsr" } From 3b5ea2e6ea58bb879e1f076db3394f15a3e40833 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:42:58 +0200 Subject: [PATCH 16/45] fix: tests, update connection namespace --- src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d19f3f5..42b617b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -304,11 +304,12 @@ mod tests { use i3ipc::reply::NodeType; use std::env; use std::error::Error; + use xcb::Connection; #[test] fn connection_tree() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = super::xcb::Connection::connect(None)?; + let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; let config = super::Config::default(); let res = super::regex::parse_config(&config)?; @@ -332,7 +333,7 @@ mod tests { #[test] fn get_title() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = super::xcb::Connection::connect(None)?; + let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; let tree = i3_conn.get_tree()?; let mut ids: Vec = Vec::new(); @@ -364,7 +365,7 @@ mod tests { #[test] fn collect_titles() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = super::xcb::Connection::connect(None)?; + let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; let tree = i3_conn.get_tree()?; let workspaces = super::get_workspaces(tree); From b479459ceefac3d461fb61dd21d63f9d7a2efdf5 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:45:45 +0200 Subject: [PATCH 17/45] fix: clean cache on vagrant machine Prevents warnings about incremental cache + being good form --- script/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/run_tests.sh b/script/run_tests.sh index 02969cc..24a6ce6 100755 --- a/script/run_tests.sh +++ b/script/run_tests.sh @@ -1,3 +1,3 @@ vagrant up -vagrant ssh -c "cd /vagrant; script/setup.sh; cargo test" +vagrant ssh -c "cd /vagrant; script/setup.sh; cargo clean; cargo test" vagrant halt From e74f3b06cb1f2f0701753a3d86eb09fce1e6dbbd Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:46:28 +0200 Subject: [PATCH 18/45] deps: update serde (1.0.171) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4c55e55..a255746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ travis-ci = { repository = "roosta/i3wsr" } xcb = "1.2.1" clap = { version = "4.3.11", features = ["derive"] } toml = "0.7.6" -serde = { version = "1.0.111", features = ["derive"] } +serde = { version = "1.0.171", features = ["derive"] } itertools = "0.9.0" regex = "1" encoding = "0.2" From aa812f6d3afcc500c08caf546d755c6349976ea3 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:48:36 +0200 Subject: [PATCH 19/45] deps: update itertools (0.11.0) --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cae282..9a203b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -334,9 +334,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.9.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" dependencies = [ "either", ] diff --git a/Cargo.toml b/Cargo.toml index a255746..26d2fe8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ xcb = "1.2.1" clap = { version = "4.3.11", features = ["derive"] } toml = "0.7.6" serde = { version = "1.0.171", features = ["derive"] } -itertools = "0.9.0" +itertools = "0.11.0" regex = "1" encoding = "0.2" dirs = "5.0.1" From 9d3adb007fa4d0cb1a5241f08c44126be2addc06 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:50:14 +0200 Subject: [PATCH 20/45] deps: pin regex to 1.9.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 26d2fe8..6f86d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ clap = { version = "4.3.11", features = ["derive"] } toml = "0.7.6" serde = { version = "1.0.171", features = ["derive"] } itertools = "0.11.0" -regex = "1" +regex = "1.9.1" encoding = "0.2" dirs = "5.0.1" i3ipc = "0.10.1" From 99e39c5ef8c39d6798af217964dba6d1891c53e2 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:52:12 +0200 Subject: [PATCH 21/45] deps: pin endoing to 0.2.33 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6f86d0d..3d41514 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ toml = "0.7.6" serde = { version = "1.0.171", features = ["derive"] } itertools = "0.11.0" regex = "1.9.1" -encoding = "0.2" +encoding = "0.2.33" dirs = "5.0.1" i3ipc = "0.10.1" From 9c22b5dda3abcf3eff12a5b6408c3e519ebec6bd Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 19:57:28 +0200 Subject: [PATCH 22/45] fix: format source files using rustfmt --- src/config.rs | 4 ++-- src/icons.rs | 18 ++++++++-------- src/lib.rs | 57 +++++++++++++++++++++--------------------------- src/main.rs | 60 +++++++++++++++++++++++++-------------------------- src/regex.rs | 2 +- 5 files changed, 66 insertions(+), 75 deletions(-) diff --git a/src/config.rs b/src/config.rs index 676503c..1ceee54 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,7 +9,7 @@ use std::error::Error; #[derive(Deserialize)] #[serde(default)] pub struct Config { - pub icons: HashMap, + pub icons: HashMap, pub aliases: HashMap, pub general: HashMap, pub options: HashMap, @@ -32,7 +32,7 @@ impl Config { impl Default for Config { fn default() -> Self { Config { - icons: HashMap::new(), + icons: HashMap::new(), aliases: HashMap::new(), general: HashMap::new(), options: HashMap::new(), diff --git a/src/icons.rs b/src/icons.rs index 32c47d9..5a2ee0d 100644 --- a/src/icons.rs +++ b/src/icons.rs @@ -1,19 +1,19 @@ -use std::collections::HashMap; use std::char; +use std::collections::HashMap; pub fn get_icons(name: &str) -> HashMap { match name { "awesome" => { - return HashMap::from([ - ("Firefox".to_string(), ''), - ("TelegramDesktop".to_string(), ''), - ("Alacritty".to_string(), ''), - ("Thunderbird".to_string(), ''), - ("KeeWeb".to_string(), ''), + return HashMap::from([ + ("Firefox".to_string(), ''), + ("TelegramDesktop".to_string(), ''), + ("Alacritty".to_string(), ''), + ("Thunderbird".to_string(), ''), + ("KeeWeb".to_string(), ''), ("Org.gnome.Nautilus".to_string(), ''), - ("Evince".to_string(), '') + ("Evince".to_string(), ''), ]); - }, + } _ => HashMap::new(), } } diff --git a/src/lib.rs b/src/lib.rs index 42b617b..1d098f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -use xcb::{x, XidNew}; +use encoding::all::ISO_8859_1; +use encoding::{DecoderTrap, Encoding}; use i3ipc::{ event::{ inner::{WindowChange, WorkspaceChange}, @@ -7,16 +8,15 @@ use i3ipc::{ reply::{Node, NodeType}, I3Connection, }; -use encoding::{Encoding, DecoderTrap}; -use encoding::all::ISO_8859_1; use itertools::Itertools; +use xcb::{x, XidNew}; pub mod config; pub mod icons; pub mod regex; -use std::error::Error; use config::Config; +use std::error::Error; /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { @@ -32,8 +32,7 @@ fn get_property( id: u32, property: x::Atom, ) -> Result> { - - let window = unsafe{ XidNew::new(id) }; + let window = unsafe { XidNew::new(id) }; let cookie = conn.send_request(&x::GetProperty { delete: false, window, @@ -62,7 +61,6 @@ fn get_title( config: &Config, res: &Vec, ) -> Result> { - let use_prop = match config.general.get("wm_property") { Some(prop) => prop, None => "class", @@ -73,13 +71,11 @@ fn get_title( // Store wm_class // use pattern matching for vector slice to extract class depending on position - let [ wm_class, wm_instance ] = match result[..] { - [class] => [ class, "" ], - [instance, class] => [ class, instance ], - [instance, class, ..] => [ class, instance ], - _ => { - Err(format!("failed to get a instance for window id {}", id))? - } + let [wm_class, wm_instance] = match result[..] { + [class] => [class, ""], + [instance, class] => [class, instance], + [instance, class, ..] => [class, instance], + _ => Err(format!("failed to get a instance for window id {}", id))?, }; // Store window name, fall back to class @@ -100,20 +96,17 @@ fn get_title( } else { wm_instance.to_string() } - - }, + } "name" => wm_name, - _ => wm_class.to_string() + _ => wm_class.to_string(), }; // Check for aliases using pre-compiled regex let title = { - let mut filtered = res.iter().filter(|(re, _)| { - re.is_match(&target) - }); + let mut filtered = res.iter().filter(|(re, _)| re.is_match(&target)); match filtered.next() { Some((_, alias)) => alias, - None => &target + None => &target, } }; @@ -131,7 +124,7 @@ fn get_title( } None => match config.general.get("default_icon") { Some(default_icon) => { - if no_icon_names || no_names { + if no_icon_names || no_names { format!("{}", default_icon) } else { format!("{} {}", default_icon, title) @@ -188,7 +181,6 @@ fn collect_titles( config: &Config, res: &Vec, ) -> Vec { - let window_ids = { let mut f = get_ids(vec![workspace.floating_nodes.iter().collect()]); let mut n = get_ids(vec![workspace.nodes.iter().collect()]); @@ -232,7 +224,10 @@ pub fn update_tree( titles }; let titles = if get_option(&config, "no_names") { - titles.into_iter().filter(|s| !s.is_empty()).collect::>() + titles + .into_iter() + .filter(|s| !s.is_empty()) + .collect::>() } else { titles }; @@ -242,14 +237,12 @@ pub fn update_tree( } else { titles }; - let old: String = workspace.name - .to_owned() - .ok_or_else(|| { - format!( - "Failed to get workspace name for workspace: {:#?}", - workspace - ) - })?; + let old: String = workspace.name.to_owned().ok_or_else(|| { + format!( + "Failed to get workspace name for workspace: {:#?}", + workspace + ) + })?; let mut new = old.split(' ').next().unwrap().to_owned(); diff --git a/src/main.rs b/src/main.rs index 2b70617..512f48e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,9 @@ -use std::{path::Path}; +use clap::{Parser, ValueEnum}; use dirs::config_dir; -use i3ipc::{ - event::Event, - I3Connection, - I3EventListener, - Subscription -}; +use i3ipc::{event::Event, I3Connection, I3EventListener, Subscription}; +use i3wsr::config::Config; use std::error::Error; -use i3wsr::config::{Config}; -use clap::{Parser, ValueEnum}; +use std::path::Path; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] enum Icons { @@ -19,14 +14,13 @@ enum Icons { enum Properties { Class, Instance, - Name + Name, } /// i3wsr config #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { - /// Path to toml config file #[arg(short, long)] config: Option, @@ -49,8 +43,7 @@ struct Args { /// Which window property to use #[arg(short = 'p', long)] - wm_property: Option - + wm_property: Option, } /// Setup program by handling args and populating config @@ -61,12 +54,10 @@ fn setup() -> Result> { // icons // Not really that useful this opt but keeping for posterity let icons = match args.icons { - Some(icons) => { - match icons { - Icons::Awesome => "awesome", - } + Some(icons) => match icons { + Icons::Awesome => "awesome", }, - None => "" + None => "", }; // handle config @@ -75,7 +66,7 @@ fn setup() -> Result> { Some(filename) => { println!("{filename}"); Config::new(Path::new(filename), icons) - }, + } None => { if (xdg_config).exists() { Config::new(&xdg_config, icons) @@ -92,7 +83,9 @@ fn setup() -> Result> { // Flags if args.no_icon_names { - config.options.insert("no_icon_names".to_string(), args.no_icon_names); + config + .options + .insert("no_icon_names".to_string(), args.no_icon_names); } if args.no_names { @@ -100,21 +93,23 @@ fn setup() -> Result> { } if args.remove_duplicates { - config.options.insert("remove_duplicates".to_string(), args.remove_duplicates); + config + .options + .insert("remove_duplicates".to_string(), args.remove_duplicates); } // wm property let wm_property = match args.wm_property { - Some(prop) => { - match prop { - Properties::Class => String::from("class"), - Properties::Instance => String::from("instance"), - Properties::Name => String::from("name") - } + Some(prop) => match prop { + Properties::Class => String::from("class"), + Properties::Instance => String::from("instance"), + Properties::Name => String::from("name"), }, - None => String::from("class") + None => String::from("class"), }; - config.general.insert("wm_property".to_string(), wm_property); + config + .general + .insert("wm_property".to_string(), wm_property); Ok(config) } @@ -134,12 +129,15 @@ fn main() -> Result<(), Box> { for event in listener.listen() { match event? { Event::WindowEvent(e) => { - if let Err(error) = i3wsr::handle_window_event(&e, &x_conn, &mut i3_conn, &config, &res) { + if let Err(error) = + i3wsr::handle_window_event(&e, &x_conn, &mut i3_conn, &config, &res) + { eprintln!("handle_window_event error: {}", error); } } Event::WorkspaceEvent(e) => { - if let Err(error) = i3wsr::handle_ws_event(&e, &x_conn, &mut i3_conn, &config, &res) { + if let Err(error) = i3wsr::handle_ws_event(&e, &x_conn, &mut i3_conn, &config, &res) + { eprintln!("handle_ws_event error: {}", error); } } diff --git a/src/regex.rs b/src/regex.rs index 84524f0..ea4e5e2 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -1,5 +1,5 @@ -use regex::Regex; use crate::Config; +use regex::Regex; use std::error::Error; pub type Point = (Regex, String); From f35d9cd61e607cd438a6406e027d49dbf0562f7a Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 20:07:02 +0200 Subject: [PATCH 23/45] fix: refresh lock file --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9a203b6..a61129c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,9 +92,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.3.11" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73" dependencies = [ "clap_builder", "clap_derive", @@ -103,9 +103,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.11" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd" dependencies = [ "anstream", "anstyle", @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.2" +version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ "heck", "proc-macro2", From 543966db4f58d08bb57ed9449b841548eb883700 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Fri, 14 Jul 2023 20:07:31 +0200 Subject: [PATCH 24/45] fix: license year to current --- LICENSE | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 15d2fb9..a5a1e4e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Daniel Berg +Copyright (c) 2023 Daniel Berg Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. From 1310e911123653ceef65c30123e7a528728ba4b1 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 15 Jul 2023 15:11:06 +0200 Subject: [PATCH 25/45] fix: ignore scratch buffer --- src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1d098f7..2c1ec0f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -149,7 +149,14 @@ fn get_workspaces(tree: Node) -> Vec { for container in output.nodes { for workspace in container.nodes { if let NodeType::Workspace = workspace.nodetype { - out.push(workspace); + match &workspace.name { + Some(name) => { + if !name.eq("__i3_scratch") { + out.push(workspace); + } + } + None => (), + } } } } From 1782f3db622388080db74db55b5b6edf2112a016 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 15 Jul 2023 18:17:16 +0200 Subject: [PATCH 26/45] feat: add split_at option Closes #33 wip: add basic split_at handling + opt possible array indexing panic wip: remove unneeded compiled regex wip: remove array indexing, update comments wip: update docs --- README.md | 41 ++++++++++++++++++++++++++++++-------- assets/example_config.toml | 1 + src/lib.rs | 41 ++++++++++++++++++++++++++++++++------ src/main.rs | 9 +++++++++ 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index b9f8e4a..ca90c4b 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ i3wsr - i3 workspace renamer ====== -[![Build Status](https://app.travis-ci.com/roosta/i3wsr.svg?branch=master)](https://app.travis-ci.com/roosta/i3wsr) +[![Build Status](https://app.travis-ci.com/roosta/i3wsr.svg?branch=main)](https://app.travis-ci.com/roosta/i3wsr) [![Crates.io](https://img.shields.io/crates/v/i3wsr)](https://crates.io/crates/i3wsr) `i3wsr` is a small program that uses [I3's](https://i3wm.org/) [IPC Interface](https://i3wm.org/docs/ipc.html) to change the name of a workspace based on its contents. + + ## Table of content * [Installation](#installation) @@ -33,7 +35,7 @@ to change the name of a workspace based on its contents. The chosen name for a workspace is a composite of the `WM_CLASS` X11 window property for each window in a workspace. In action it would look something like this: -![](https://raw.githubusercontent.com/roosta/i3wsr/master/assets/preview.gif) +![](https://raw.githubusercontent.com/roosta/i3wsr/main/assets/preview.gif) ## Requirements i3wsr requires [XCB](https://xcb.freedesktop.org/), if you get compilation @@ -88,12 +90,15 @@ bindsym $mod+1 workspace number 1 assign [class="(?i)firefox"] number 1 ``` -If you're like me and don't necessarily bind your workspaces to only numbers, or -you want to keep a part of the name constant you can do like this: +### Keeping part of the workspace name + +If you're like me and don't necessarily bind your workspaces to only numbers, +or you want to keep a part of the name constant you can do like this: ``` -bindsym $mod+q workspace number 1:[Q] -assign [class="(?i)firefox"] number 1:[Q] +set $myws "1:[Q]" # my sticky part +bindsym $mod+q workspace number $myws +assign [class="(?i)firefox"] number $myws ``` This way the workspace would look something like this when it gets changed: @@ -117,7 +122,7 @@ To specify another path, pass it to the `--config` option on invocation: i3wsr --config ~/my_config.toml ``` Example config can be found in -[assets/example_config.toml](https://github.com/roosta/i3wsr/blob/master/assets/example_config.toml). +[assets/example\_config.toml](https://github.com/roosta/i3wsr/blob/main/assets/example_config.toml). ### Aliases @@ -287,8 +292,28 @@ file: remove_duplicates = true ``` +### Split at character + +By default i3wsr will keep everything until the first `space` character is found, +then replace the remainder with titles. + +If you want to define a different character that is used to split the +numbered/constant part of the workspace and the dynamic content, you can use +the option `--split-at [CHAR]` + +```toml +[general] +split_at = ":" +``` + +Here we define colon as the split character, which results in i3wsr only +keeping the numbered part of a workspace name when renaming. + +This can give a cleaner config, but I've kept the old behavior as default. + + ## Sway -Check [Pedro Scaff](https://github.com/pedroscaff)'s port [swaywsr](https://github.com/pedroscaff/swaywsr). + Check [Pedro Scaff](https://github.com/pedroscaff)'s port [swaywsr](https://github.com/pedroscaff/swaywsr). ## Testing diff --git a/assets/example_config.toml b/assets/example_config.toml index 824cf27..29a6d5e 100644 --- a/assets/example_config.toml +++ b/assets/example_config.toml @@ -18,6 +18,7 @@ TelegramDesktop = "Telegram" separator = "  " default_icon = "" wm_property = "class" +split_at = ":" [options] remove_duplicates = false diff --git a/src/lib.rs b/src/lib.rs index 2c1ec0f..7dcfd6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,10 @@ use xcb::{x, XidNew}; pub mod config; pub mod icons; pub mod regex; - use config::Config; use std::error::Error; + /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { return match config.options.get(key) { @@ -215,7 +215,7 @@ pub fn update_tree( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec, + res: &Vec ) -> Result<(), Box> { let tree = i3_conn.get_tree()?; for workspace in get_workspaces(tree) { @@ -251,12 +251,40 @@ pub fn update_tree( ) })?; - let mut new = old.split(' ').next().unwrap().to_owned(); + // Get split_at arg + let split_at = match config.general.get("split_at") { + Some(s) => { + if !s.is_empty() { + s.chars().next().unwrap() + } else { + ' ' + } + + }, + None => ' ', + }; + + // Get the initial element we want to keep + let initial = match old.split(split_at).next() { + Some(i) => i, + None => "" + }; + + let mut new: String = String::from(initial); + + // if we do split on colon we need to insert a new one, cause it gets split out + if split_at == ':' && !initial.is_empty() { + new.push(':'); + } else { + + } + // Push new window titles to new string if !titles.is_empty() { new.push_str(&titles); } + // Dispatch to i3 if old != new { let command = format!("rename workspace \"{}\" to \"{}\"", old, new); i3_conn.run_command(&command)?; @@ -271,7 +299,7 @@ pub fn handle_window_event( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec, + res: &Vec ) -> Result<(), Box> { match e.change { WindowChange::New | WindowChange::Close | WindowChange::Move | WindowChange::Title => { @@ -288,7 +316,7 @@ pub fn handle_ws_event( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec, + res: &Vec ) -> Result<(), Box> { match e.change { WorkspaceChange::Empty | WorkspaceChange::Focus => { @@ -313,7 +341,8 @@ mod tests { let mut i3_conn = super::I3Connection::connect()?; let config = super::Config::default(); let res = super::regex::parse_config(&config)?; - assert!(super::update_tree(&x_conn, &mut i3_conn, &config, &res).is_ok()); + let digit_re = regex::Regex::new(r"\d*$").unwrap(); + assert!(super::update_tree(&x_conn, &mut i3_conn, &config, &res, &digit_re).is_ok()); let tree = i3_conn.get_tree()?; let mut name: String = String::new(); for output in &tree.nodes { diff --git a/src/main.rs b/src/main.rs index 512f48e..919f246 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,10 @@ struct Args { /// Which window property to use #[arg(short = 'p', long)] wm_property: Option, + + /// What character used to split the workspace title string + #[arg(short = 'a', long)] + split_at: Option } /// Setup program by handling args and populating config @@ -98,6 +102,10 @@ fn setup() -> Result> { .insert("remove_duplicates".to_string(), args.remove_duplicates); } + if let Some(split_char) = args.split_at { + config.general.insert("split_at".to_string(), split_char); + } + // wm property let wm_property = match args.wm_property { Some(prop) => match prop { @@ -120,6 +128,7 @@ fn main() -> Result<(), Box> { let res = i3wsr::regex::parse_config(&config)?; let mut listener = I3EventListener::connect()?; let subs = [Subscription::Window, Subscription::Workspace]; + listener.subscribe(&subs)?; let (x_conn, _) = xcb::Connection::connect(None)?; From c2fe1a95b00160842834b07f86842e17f5a50841 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 15 Jul 2023 19:08:47 +0200 Subject: [PATCH 27/45] fix: tests Remove leftover update_tree signature, fix expected result in collect_titles test, since we filter out the scratch buffer, no need for that empty vector initially --- src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7dcfd6a..d8c227b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -341,8 +341,7 @@ mod tests { let mut i3_conn = super::I3Connection::connect()?; let config = super::Config::default(); let res = super::regex::parse_config(&config)?; - let digit_re = regex::Regex::new(r"\d*$").unwrap(); - assert!(super::update_tree(&x_conn, &mut i3_conn, &config, &res, &digit_re).is_ok()); + assert!(super::update_tree(&x_conn, &mut i3_conn, &config, &res).is_ok()); let tree = i3_conn.get_tree()?; let mut name: String = String::new(); for output in &tree.nodes { @@ -404,7 +403,7 @@ mod tests { for workspace in workspaces { result.push(super::collect_titles(&workspace, &x_conn, &config, &res)); } - let expected = vec![vec![], vec!["Gpick", "XTerm"]]; + let expected = vec![vec!["Gpick", "XTerm"]]; assert_eq!(result, expected); Ok(()) } From 42142b44a14d1d8bd46fb264c1a49cfcbb58aa0e Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Tue, 18 Jul 2023 15:31:17 +0200 Subject: [PATCH 28/45] doc: update toc --- README.md | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index ca90c4b..27fb98e 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,35 @@ i3wsr - i3 workspace renamer `i3wsr` is a small program that uses [I3's](https://i3wm.org/) [IPC Interface](https://i3wm.org/docs/ipc.html) to change the name of a workspace based on its contents. +# TOC + +- [i3wsr - i3 workspace renamer](#i3wsr---i3-workspace-renamer) +- [TOC](#toc) + - [Details](#details) + - [Requirements](#requirements) + - [Installation](#installation) + - [Arch linux](#arch-linux) + - [Usage](#usage) + - [i3 configuration](#i3-configuration) + - [Keeping part of the workspace name](#keeping-part-of-the-workspace-name) + - [Configuration / options](#configuration--options) + - [Aliases](#aliases) + - [WM Property](#wm-property) + - [Class](#class) + - [Instance](#instance) + - [Name](#name) + - [Icons](#icons) + - [Separator](#separator) + - [Default icon](#default-icon) + - [No icon names](#no-icon-names) + - [No names](#no-names) + - [Remove duplicates](#remove-duplicates) + - [Split at character](#split-at-character) + - [Sway](#sway) + - [Testing](#testing) + - [Attribution](#attribution) -## Table of content - -* [Installation](#installation) - * [Arch linux](#arch-linux) -* [Usage](#usage) -* [i3 configuration](#i3-configuration) -* [Configuration / options](#configuration--options) - * [Aliases](#aliases) - * [WM Property](#wm-property) - * [Class](#class) - * [Instance](#instance) - * [Name](#name) - * [Icons](#icons) - * [Separator](#separator) - * [Default icon](#default-icon) - * [No icon names](#no-icon-names) - * [No names](#no-names) - * [Remove duplicates](#remove-duplicates) -* [Sway](#sway) -* [Testing](#testing) -* [Attribution](#attribution) - ## Details The chosen name for a workspace is a composite of the `WM_CLASS` X11 window From 6e51ba25f1afcf4f8426c1847d73247782f15d7d Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Wed, 6 Sep 2023 16:44:44 +0200 Subject: [PATCH 29/45] feat!: enable wm_property scoped aliases BREAKING CHANGE: Change the [aliases] to have sub tables, one for each wm_property (name, instance, class). i3wsr will check names first, then instance, and lastly class, and if no alias can be found use wm_class DEPRECATED: general/wm_property has been deprecated in favor of alias sub tables Config needs to be changed to match this change: [aliases] => [aliases.name], [aliases.instance], [aliases.class] If you just used class to match aliases replace [aliases] with [aliases.class]. Just match your previous wm_property with a sub table. --- src/config.rs | 27 +++++++++++++++++++++--- src/lib.rs | 58 ++++++++++++++++++++++++++++++--------------------- src/regex.rs | 25 ++++++++++++++++++++-- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/config.rs b/src/config.rs index 1ceee54..4652f69 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,14 +3,21 @@ use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::path::Path; - use std::error::Error; +#[derive(Deserialize)] +#[serde(default)] +pub struct Aliases { + pub class: HashMap, + pub instance: HashMap, + pub name: HashMap +} + #[derive(Deserialize)] #[serde(default)] pub struct Config { pub icons: HashMap, - pub aliases: HashMap, + pub aliases: Aliases, pub general: HashMap, pub options: HashMap, } @@ -29,11 +36,25 @@ impl Config { } } +impl Default for Aliases { + fn default() -> Self { + Aliases { + class: HashMap::new(), + instance: HashMap::new(), + name: HashMap::new() + } + } +} + impl Default for Config { fn default() -> Self { Config { icons: HashMap::new(), - aliases: HashMap::new(), + aliases: Aliases { + class: HashMap::new(), + instance: HashMap::new(), + name: HashMap::new(), + }, general: HashMap::new(), options: HashMap::new(), } diff --git a/src/lib.rs b/src/lib.rs index d8c227b..dd7b106 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,12 +59,8 @@ fn get_title( conn: &xcb::Connection, id: u32, config: &Config, - res: &Vec, + res: ®ex::Compiled ) -> Result> { - let use_prop = match config.general.get("wm_property") { - Some(prop) => prop, - None => "class", - }; let reply = get_property(&conn, id, x::ATOM_WM_CLASS)?; let result: Vec<&str> = reply.split('\0').collect(); @@ -88,25 +84,39 @@ fn get_title( } }; - // Set target from options - let target = match use_prop { - "instance" => { - if wm_instance.is_empty() { - wm_class.to_string() - } else { - wm_instance.to_string() - } - } - "name" => wm_name, - _ => wm_class.to_string(), - }; + // // Set target from options + // let target = match use_prop { + // "instance" => { + // if wm_instance.is_empty() { + // wm_class.to_string() + // } else { + // wm_instance.to_string() + // } + // } + // "name" => wm_name, + // _ => wm_class.to_string(), + // }; // Check for aliases using pre-compiled regex let title = { - let mut filtered = res.iter().filter(|(re, _)| re.is_match(&target)); - match filtered.next() { + let mut filtered_classes = + res.class.iter().filter(|(re, _)| re.is_match(&wm_class)); + + let mut filtered_instances = + res.instance.iter().filter(|(re, _)| re.is_match(&wm_instance)); + + let mut filtered_names = + res.name.iter().filter(|(re, _)| re.is_match(&wm_name)); + + match filtered_names.next() { Some((_, alias)) => alias, - None => &target, + None => match filtered_instances.next() { + Some((_, alias)) => alias, + None => match filtered_classes.next() { + Some((_, alias)) => alias, + None => wm_class + } + } } }; @@ -186,7 +196,7 @@ fn collect_titles( workspace: &Node, x_conn: &xcb::Connection, config: &Config, - res: &Vec, + res: ®ex::Compiled, ) -> Vec { let window_ids = { let mut f = get_ids(vec![workspace.floating_nodes.iter().collect()]); @@ -215,7 +225,7 @@ pub fn update_tree( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec + res: ®ex::Compiled ) -> Result<(), Box> { let tree = i3_conn.get_tree()?; for workspace in get_workspaces(tree) { @@ -299,7 +309,7 @@ pub fn handle_window_event( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec + res: ®ex::Compiled ) -> Result<(), Box> { match e.change { WindowChange::New | WindowChange::Close | WindowChange::Move | WindowChange::Title => { @@ -316,7 +326,7 @@ pub fn handle_ws_event( x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, - res: &Vec + res: ®ex::Compiled ) -> Result<(), Box> { match e.change { WorkspaceChange::Empty | WorkspaceChange::Focus => { diff --git a/src/regex.rs b/src/regex.rs index ea4e5e2..dbccad0 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -3,12 +3,33 @@ use regex::Regex; use std::error::Error; pub type Point = (Regex, String); +pub struct Compiled { + pub class: Vec, + pub instance: Vec, + pub name: Vec +} fn compile((k, v): (&String, &String)) -> Result> { let re = Regex::new(&format!(r"{}", k))?; Ok((re, v.to_owned())) } -pub fn parse_config(config: &Config) -> Result, Box> { - config.aliases.iter().map(compile).collect() +pub fn parse_config(config: &Config) -> Result> { + let classes = match config.aliases.class.iter().map(compile).collect() { + Ok(v) => v, + Err(e) => Err(e)? + }; + let instances = match config.aliases.instance.iter().map(compile).collect() { + Ok(v) => v, + Err(e) => Err(e)? + }; + let names = match config.aliases.name.iter().map(compile).collect() { + Ok(v) => v, + Err(e) => Err(e)? + }; + return Ok(Compiled { + class: classes, + instance: instances, + name: names + }) } From 04ce4e94b5db9a03f6d39307a80dae4fe79dd69e Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Wed, 1 Nov 2023 15:36:30 +0100 Subject: [PATCH 30/45] feat: add empty_label option Enables setting a custom label on empty workspaces. Needs to go in config under [general], empty_label = "mystring" ref: https://github.com/roosta/i3wsr/issues/35 --- README.md | 7 +++++++ assets/example_config.toml | 1 + src/lib.rs | 10 ++++++++++ 3 files changed, 18 insertions(+) diff --git a/README.md b/README.md index 27fb98e..a1c4881 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,14 @@ To use a default icon when no other is defined use: [general] default_icon = "💀" ``` +### Empty label +Set a label for empty workspaces. + +```toml +[general] +empty_label = "🌕" +``` ### No icon names To display names only if icon is not available, you can use the `--no-icon-names` flag, or enable it in your config file like so: diff --git a/assets/example_config.toml b/assets/example_config.toml index 29a6d5e..c24d9b3 100644 --- a/assets/example_config.toml +++ b/assets/example_config.toml @@ -19,6 +19,7 @@ separator = "  " default_icon = "" wm_property = "class" split_at = ":" +empty_label = "🌕" [options] remove_duplicates = false diff --git a/src/lib.rs b/src/lib.rs index dd7b106..c7fe4fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -294,6 +294,16 @@ pub fn update_tree( new.push_str(&titles); } + if titles.is_empty() { + match config.general.get("empty_label") { + Some(default_label) => { + new.push_str(" "); + new.push_str(default_label); + } + None => () + } + } + // Dispatch to i3 if old != new { let command = format!("rename workspace \"{}\" to \"{}\"", old, new); From 38d642a244391d0f2137d0f7f7091e9feea0e2ec Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Tue, 7 Nov 2023 15:07:50 +0100 Subject: [PATCH 31/45] Remove commented unused code --- src/lib.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c7fe4fc..0ce41c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,19 +84,6 @@ fn get_title( } }; - // // Set target from options - // let target = match use_prop { - // "instance" => { - // if wm_instance.is_empty() { - // wm_class.to_string() - // } else { - // wm_instance.to_string() - // } - // } - // "name" => wm_name, - // _ => wm_class.to_string(), - // }; - // Check for aliases using pre-compiled regex let title = { let mut filtered_classes = @@ -270,7 +257,6 @@ pub fn update_tree( } else { ' ' } - }, None => ' ', }; From 5abc4e0f4f30527d9989ba389c0425ef17594c07 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Wed, 8 Nov 2023 17:37:28 +0100 Subject: [PATCH 32/45] Remove leftover branch --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0ce41c6..4b32b76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -272,8 +272,6 @@ pub fn update_tree( // if we do split on colon we need to insert a new one, cause it gets split out if split_at == ':' && !initial.is_empty() { new.push(':'); - } else { - } // Push new window titles to new string if !titles.is_empty() { From a80e19bcf0dbff12e94a3d63f38d6f434272d38c Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Wed, 8 Nov 2023 19:07:40 +0100 Subject: [PATCH 33/45] refactor: replace xcb with i3ipc window_properties This refactor uses my fork of i3ipc-rs, which includes a fix that makes i3ipc-rs return the correct window_properties, which includes class, instance, and name/title. This makes XCB no longer needed as a dependency, which in turn makes the encoding handling unneeded. Reason I use my own fork, instead of a PR is that the upstream i3ipc-rs lib hasn't been updated for a while, and there are a multitude of PR requests that goes unhandled, including a fix for my own issue. Will dispatch of my own fork if things get moving again. wip: use local i3ipc package wip wip: setup testing functions wip: get props wip: parse props Add log crate, use git source for i3ipc I did a quick fix for an issue with window_properties wip new filtering logic refactor: add proof of concept _get_title impl refactor: cleanup old code + deps wip: tests --- Cargo.lock | 89 +------------------- Cargo.toml | 7 +- README.md | 18 ++--- script/vagrant_root.sh | 2 +- src/lib.rs | 180 ++++++++++++++--------------------------- src/main.rs | 7 +- 6 files changed, 77 insertions(+), 226 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a61129c..51b913f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,70 +164,6 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - [[package]] name = "equivalent" version = "1.0.1" @@ -287,8 +223,7 @@ checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "i3ipc" version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63f3dac00c473fae88cb3114f35312204469a32ffb20874264a5214d6c8c927e" +source = "git+https://github.com/roosta/i3ipc-rs#7aad30d162dc4fd8a649c959c9ad888a646e07f2" dependencies = [ "byteorder", "log", @@ -302,13 +237,11 @@ version = "2.1.1" dependencies = [ "clap", "dirs", - "encoding", "i3ipc", "itertools", "regex", "serde", "toml", - "xcb", ] [[package]] @@ -392,15 +325,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" -dependencies = [ - "memchr", -] - [[package]] name = "quote" version = "1.0.29" @@ -681,14 +605,3 @@ checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7" dependencies = [ "memchr", ] - -[[package]] -name = "xcb" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b90c622d513012e7419594a2138953603c63848cb189041e7b5dc04d3895da5" -dependencies = [ - "bitflags 1.3.2", - "libc", - "quick-xml", -] diff --git a/Cargo.toml b/Cargo.toml index 3d41514..3ea9c69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,13 +16,14 @@ exclude = ["/script", "/assets/*", "Vagrantfile"] travis-ci = { repository = "roosta/i3wsr" } [dependencies] -xcb = "1.2.1" clap = { version = "4.3.11", features = ["derive"] } toml = "0.7.6" serde = { version = "1.0.171", features = ["derive"] } itertools = "0.11.0" regex = "1.9.1" -encoding = "0.2.33" dirs = "5.0.1" -i3ipc = "0.10.1" +# log = "0.4" +[dependencies.i3ipc] +git = 'https://github.com/roosta/i3ipc-rs' +# path = "../i3ipc-rs" diff --git a/README.md b/README.md index a1c4881..ebd5db6 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,9 @@ property for each window in a workspace. In action it would look something like ![](https://raw.githubusercontent.com/roosta/i3wsr/main/assets/preview.gif) ## Requirements -i3wsr requires [XCB](https://xcb.freedesktop.org/), if you get compilation -errors mentioning `xcb`, you might need to install `libxcb`. On Ubuntu for -example you'd install: - -```sh -sudo apt-get install libxcb1-dev -``` - -Refer to [#18](https://github.com/roosta/i3wsr/issues/18) for more. - +i3wsr requires [i3wm](https://i3wm.org/) and [numbered +workspaces](https://i3wm.org/docs/userguide.html#_changing_named_workspaces_moving_to_workspaces), +see [i3-configuration](#i3-configuration) ## Installation @@ -85,7 +78,7 @@ exec_always --no-startup-id $HOME/.cargo/bin/i3wsr exec_always --no-startup-id /usr/bin/i3wsr ``` -## i3 configuration +## i3-configuration This program depends on numbered workspaces, since we're constantly changing the workspace name. So your I3 configuration need to reflect this: @@ -333,8 +326,9 @@ To run tests locally [Vagrant](https://www.vagrantup.com/) is required. Run `script/run_tests.sh` to run tests on ubuntu xenial. ## Attribution + This program would not be possible without [i3ipc-rs](https://github.com/tmerr/i3ipc-rs), a rust library for controlling -i3-wm through its IPC interface and +i3wm through its IPC interface and [rust-xcb](https://github.com/rtbo/rust-xcb), a set of rust bindings and wrappers for [XCB](http://xcb.freedesktop.org/). diff --git a/script/vagrant_root.sh b/script/vagrant_root.sh index d49155f..c18e480 100644 --- a/script/vagrant_root.sh +++ b/script/vagrant_root.sh @@ -2,4 +2,4 @@ # Install needed packages apt-get update -apt-get install -y i3-wm gcc gpick xterm xvfb libxcb1-dev +apt-get install -y i3-wm gcc gpick xterm xvfb diff --git a/src/lib.rs b/src/lib.rs index 4b32b76..cba5b42 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,13 @@ -use encoding::all::ISO_8859_1; -use encoding::{DecoderTrap, Encoding}; +use std::collections::HashMap; use i3ipc::{ event::{ inner::{WindowChange, WorkspaceChange}, WindowEventInfo, WorkspaceEventInfo, }, - reply::{Node, NodeType}, + reply::{Node, NodeType, WindowProperty}, I3Connection, }; use itertools::Itertools; -use xcb::{x, XidNew}; pub mod config; pub mod icons; @@ -26,84 +24,32 @@ fn get_option(config: &Config, key: &str) -> bool { }; } -/// Return window property based on id. -fn get_property( - conn: &xcb::Connection, - id: u32, - property: x::Atom, -) -> Result> { - let window = unsafe { XidNew::new(id) }; - let cookie = conn.send_request(&x::GetProperty { - delete: false, - window, - property, - r#type: x::ATOM_STRING, - long_offset: 0, - long_length: 1024, - }); - - let reply = conn.wait_for_reply(cookie)?; - if let Ok(s) = std::str::from_utf8(reply.value()) { - Ok(s.to_string()) - } else { - let decoded = ISO_8859_1.decode(reply.value(), DecoderTrap::Strict); - match decoded { - Ok(s) => Ok(s), - Err(_) => Ok(String::new()), - } - } -} - /// Gets a window title, depends on wm_property config opt fn get_title( - conn: &xcb::Connection, - id: u32, + props: &HashMap, config: &Config, res: ®ex::Compiled ) -> Result> { - let reply = get_property(&conn, id, x::ATOM_WM_CLASS)?; - let result: Vec<&str> = reply.split('\0').collect(); - - // Store wm_class - // use pattern matching for vector slice to extract class depending on position - let [wm_class, wm_instance] = match result[..] { - [class] => [class, ""], - [instance, class] => [class, instance], - [instance, class, ..] => [class, instance], - _ => Err(format!("failed to get a instance for window id {}", id))?, - }; - - // Store window name, fall back to class - let wm_name = { - let name = get_property(&conn, id, x::ATOM_WM_NAME)?; - if name.is_empty() { - wm_class.to_string() - } else { - name - } - }; + let wm_class = props.get(&WindowProperty::Class); + let wm_instance = props.get(&WindowProperty::Instance); + let wm_name = props.get(&WindowProperty::Title); // Check for aliases using pre-compiled regex let title = { - let mut filtered_classes = - res.class.iter().filter(|(re, _)| re.is_match(&wm_class)); - - let mut filtered_instances = - res.instance.iter().filter(|(re, _)| re.is_match(&wm_instance)); - - let mut filtered_names = - res.name.iter().filter(|(re, _)| re.is_match(&wm_name)); - - match filtered_names.next() { - Some((_, alias)) => alias, - None => match filtered_instances.next() { - Some((_, alias)) => alias, - None => match filtered_classes.next() { - Some((_, alias)) => alias, - None => wm_class - } + if let Some((_, alias)) = wm_name.and_then(|name| res.name.iter().filter(|(re, _)| re.is_match(&name)).next()) { + alias + } else if let Some((_, alias)) = wm_instance.and_then(|instance| res.instance.iter().filter(|(re, _)| re.is_match(&instance)).next()) { + alias + } else if let Some((_, alias)) = wm_class.and_then(|class| res.class.iter().filter(|(re, _)| re.is_match(&class)).next()) { + alias + } else { + if let Some(class) = wm_class { + class + } else { + Err(format!("failed to get any class given these window properties {:#?}", props))? } + } }; @@ -162,39 +108,40 @@ fn get_workspaces(tree: Node) -> Vec { out } + /// get window ids for any depth collection of nodes -fn get_ids(mut nodes: Vec>) -> Vec { - let mut window_ids = Vec::new(); +fn get_properties(mut nodes: Vec>) -> Vec> { + let mut window_props = Vec::new(); while let Some(next) = nodes.pop() { for n in next { nodes.push(n.nodes.iter().collect()); - if let Some(w) = n.window { - window_ids.push(w as u32); + if let Some(w) = &n.window_properties { + window_props.push(w.to_owned()); } } } - window_ids + window_props } /// Collect a vector of workspace titles fn collect_titles( workspace: &Node, - x_conn: &xcb::Connection, config: &Config, res: ®ex::Compiled, ) -> Vec { - let window_ids = { - let mut f = get_ids(vec![workspace.floating_nodes.iter().collect()]); - let mut n = get_ids(vec![workspace.nodes.iter().collect()]); + + let window_props = { + let mut f = get_properties(vec![workspace.floating_nodes.iter().collect()]); + let mut n = get_properties(vec![workspace.nodes.iter().collect()]); n.append(&mut f); n }; let mut titles = Vec::new(); - for id in window_ids { - let title = match get_title(&x_conn, id, config, res) { + for props in window_props { + let title = match get_title(&props, config, res) { Ok(title) => title, Err(e) => { eprintln!("get_title error: {}", e); @@ -209,7 +156,6 @@ fn collect_titles( /// Update all workspace names in tree pub fn update_tree( - x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, res: ®ex::Compiled @@ -221,7 +167,7 @@ pub fn update_tree( None => " | ", }; - let titles = collect_titles(&workspace, &x_conn, config, res); + let titles = collect_titles(&workspace, config, res); let titles = if get_option(&config, "remove_duplicates") { titles.into_iter().unique().collect() } else { @@ -300,14 +246,13 @@ pub fn update_tree( /// handles new and close window events, to set the workspace name based on content pub fn handle_window_event( e: &WindowEventInfo, - x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, res: ®ex::Compiled ) -> Result<(), Box> { match e.change { WindowChange::New | WindowChange::Close | WindowChange::Move | WindowChange::Title => { - update_tree(x_conn, i3_conn, config, res)?; + update_tree(i3_conn, config, res)?; } _ => (), } @@ -317,14 +262,13 @@ pub fn handle_window_event( /// handles ws events, pub fn handle_ws_event( e: &WorkspaceEventInfo, - x_conn: &xcb::Connection, i3_conn: &mut I3Connection, config: &Config, res: ®ex::Compiled ) -> Result<(), Box> { match e.change { WorkspaceChange::Empty | WorkspaceChange::Focus => { - update_tree(x_conn, i3_conn, config, res)?; + update_tree(i3_conn, config, res)?; } _ => (), } @@ -333,19 +277,18 @@ pub fn handle_ws_event( #[cfg(test)] mod tests { - use i3ipc::reply::NodeType; - use std::env; + use i3ipc::reply::{NodeType, WindowProperty}; use std::error::Error; - use xcb::Connection; + use std::env; + use std::collections::HashMap; #[test] fn connection_tree() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; let config = super::Config::default(); let res = super::regex::parse_config(&config)?; - assert!(super::update_tree(&x_conn, &mut i3_conn, &config, &res).is_ok()); + assert!(super::update_tree(&mut i3_conn, &config, &res).is_ok()); let tree = i3_conn.get_tree()?; let mut name: String = String::new(); for output in &tree.nodes { @@ -365,30 +308,27 @@ mod tests { #[test] fn get_title() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; + let tree = i3_conn.get_tree()?; - let mut ids: Vec = Vec::new(); + let mut properties: Vec> = Vec::new(); let workspaces = super::get_workspaces(tree); for workspace in &workspaces { - for node in &workspace.nodes { - if let Some(w) = node.window { - ids.push(w as u32); - } - } - for node in &workspace.floating_nodes { - for n in &node.nodes { - if let Some(w) = n.window { - ids.push(w as u32); - } - } + let window_props = { + let mut f = super::get_properties(vec![workspace.floating_nodes.iter().collect()]); + let mut n = super::get_properties(vec![workspace.nodes.iter().collect()]); + n.append(&mut f); + n + }; + for p in window_props { + properties.push(p); } } let config = super::Config::default(); let res = super::regex::parse_config(&config)?; - let result: Result, _> = ids + let result: Result, _> = properties .iter() - .map(|id| super::get_title(&x_conn, *id, &config, &res)) + .map(|props| super::get_title(&props, &config, &res)) .collect(); assert_eq!(result?, vec!["Gpick", "XTerm"]); Ok(()) @@ -397,7 +337,6 @@ mod tests { #[test] fn collect_titles() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); - let (x_conn, _) = Connection::connect(None)?; let mut i3_conn = super::I3Connection::connect()?; let tree = i3_conn.get_tree()?; let workspaces = super::get_workspaces(tree); @@ -405,7 +344,7 @@ mod tests { let config = super::Config::default(); let res = super::regex::parse_config(&config)?; for workspace in workspaces { - result.push(super::collect_titles(&workspace, &x_conn, &config, &res)); + result.push(super::collect_titles(&workspace, &config, &res)); } let expected = vec![vec!["Gpick", "XTerm"]]; assert_eq!(result, expected); @@ -413,18 +352,23 @@ mod tests { } #[test] - fn get_ids() -> Result<(), Box> { + fn get_properties() -> Result<(), Box> { env::set_var("DISPLAY", ":99.0"); let mut i3_conn = super::I3Connection::connect()?; let tree = i3_conn.get_tree()?; let workspaces = super::get_workspaces(tree); - let mut result: Vec> = Vec::new(); + let mut result: Vec> = Vec::new(); for workspace in workspaces { - result.push(super::get_ids(vec![workspace.nodes.iter().collect()])); - result.push(super::get_ids(vec![workspace - .floating_nodes - .iter() - .collect()])); + + let window_props = { + let mut f = super::get_properties(vec![workspace.floating_nodes.iter().collect()]); + let mut n = super::get_properties(vec![workspace.nodes.iter().collect()]); + n.append(&mut f); + n + }; + for props in window_props { + result.push(props) + } } let result: usize = result.iter().filter(|v| !v.is_empty()).count(); assert_eq!(result, 2); diff --git a/src/main.rs b/src/main.rs index 919f246..9be9271 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,21 +131,20 @@ fn main() -> Result<(), Box> { listener.subscribe(&subs)?; - let (x_conn, _) = xcb::Connection::connect(None)?; let mut i3_conn = I3Connection::connect()?; - i3wsr::update_tree(&x_conn, &mut i3_conn, &config, &res)?; + i3wsr::update_tree(&mut i3_conn, &config, &res)?; for event in listener.listen() { match event? { Event::WindowEvent(e) => { if let Err(error) = - i3wsr::handle_window_event(&e, &x_conn, &mut i3_conn, &config, &res) + i3wsr::handle_window_event(&e, &mut i3_conn, &config, &res) { eprintln!("handle_window_event error: {}", error); } } Event::WorkspaceEvent(e) => { - if let Err(error) = i3wsr::handle_ws_event(&e, &x_conn, &mut i3_conn, &config, &res) + if let Err(error) = i3wsr::handle_ws_event(&e, &mut i3_conn, &config, &res) { eprintln!("handle_ws_event error: {}", error); } From c16086eefbcc81934b07e4944f802be05058c690 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 10:21:53 +0100 Subject: [PATCH 34/45] ci: add test workflow, update scripts --- .github/workflows/test.yaml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/test.yaml diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..f980377 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,22 @@ +--- +name: 'test' +on: + push: + branches: + - refactor/wm_properties + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install test dependencies + run: sudo apt-get install -y i3-wm gcc gpick xterm + + - name: Setup test environment + run: script/setup.sh + + - name: Run tests + run: cargo test From 1f13208210515b1bbea25f168c643cf14ae683c2 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 10:55:10 +0100 Subject: [PATCH 35/45] ci: update test branch --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index f980377..d168f6c 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,7 +3,7 @@ name: 'test' on: push: branches: - - refactor/wm_properties + - develop jobs: deploy: From 246efed17b4f2a0d5becb26f5f738be67bd2cd66 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 10:57:31 +0100 Subject: [PATCH 36/45] ci: fix job name --- .github/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d168f6c..c4cd4c4 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -6,7 +6,7 @@ on: - develop jobs: - deploy: + test: runs-on: ubuntu-latest steps: - name: Checkout From eb750e08b44e5e35e142dfba02dfd0669b347448 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 11:32:10 +0100 Subject: [PATCH 37/45] docs: document aliases usage --- README.md | 51 ++++++++++++++++---------------------- assets/example_config.toml | 8 ++++-- 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index ebd5db6..c7c3970 100644 --- a/README.md +++ b/README.md @@ -125,11 +125,11 @@ Example config can be found in ### Aliases -Sometimes a WM property can be overly verbose, so its possible to match a +Sometimes a WM property can be overly verbose, so its possible to match a class name with an alias: ```toml -[aliases] +[aliases.class] # Exact match "^Google-chrome-unstable$" = "Chrome-dev" @@ -155,19 +155,15 @@ rust string escapes if you want a literal backslash use two slashes `\\d`. i3wsr supports 3 window properties currently: ```toml -[general] - -wm_property = "instance" +[aliases.class] +[aliases.instance] +[aliases.name] ``` +These are checked in decending order, so if i3wsr finds a name alias, it'll use +that and if not, then check instance, then finally use class -Possible options are `class`, `instance`, and `name`, and will default to `class` -if not present. - -You can alternatively supply cmd argument: - -```sh -i3wsr --wm-property instance -``` +> Deprecation note: previously `wm_property` defined which prop to check for +> aliases, but this newer approach will allow for multiple types of aliases #### Class @@ -175,28 +171,28 @@ This is the default, and the most succinct. #### Instance -Use WM\_INSTANCE instead of WM\_CLASS when assigning workspace names, instance -is usually more specific. i3wsr will try to get the instance but if it isn't -defined will use class instead. +Use `WM_INSTANCE` instead of `WM_CLASS` when assigning workspace names, +instance is usually more specific. i3wsr will try to get the instance but if it +isn't defined will fall back to class. A use case for this option could be launching `chromium --app="https://web.whatsapp.com"`, and then assign a different icon to whatsapp -in your config file: +in your config file, while chrome retains its own alias: ```toml [icons] "WhatsApp" = "🗩" -``` -Aliases will also match on instance: -```toml -[aliases] -"web\\.whatsapp\\.com" = "WhatsApp" +[aliases.class] +Google-chrome = "Chrome" + +[aliases.instance] +"web\\.whatsapp\\.com" = "Whatsapp" ``` #### Name -Uses WM_NAME instead of WM_CLASS, this option is very verbose and relies on regex -matching of aliases to be of any use. +Uses `WM_NAME` instead of `WM_INSTANCE` and `WM_CLASS`, this option is very +verbose and relies on regex matching of aliases to be of any use. A use-case is running some terminal application, and as default i3wsr will only display class regardless of whats running in the terminal. @@ -204,10 +200,7 @@ display class regardless of whats running in the terminal. So you could do something like this: ```toml -[general] -wm_property = "name" - -[aliases] +[aliases.name] ".*mutt$" = "Mutt" ``` @@ -239,7 +232,7 @@ i3wsr tries to match an icon with an alias first, then falls back to window class, so a config like this is valid: ```toml -[aliases] +[aliases.class] "Gimp-\\d\\.\\d\\d" = "Gimp" [icons] diff --git a/assets/example_config.toml b/assets/example_config.toml index c24d9b3..8e72466 100644 --- a/assets/example_config.toml +++ b/assets/example_config.toml @@ -10,14 +10,18 @@ Nautilus = "📘" # smile emoji MyNiceProgram = "😛" -[aliases] +[aliases.class] TelegramDesktop = "Telegram" "Org\\.gnome\\.Nautilus" = "Nautilus" +[aliases.instance] +"open.spotify.com" = "Spotify" + +[aliases.name] + [general] separator = "  " default_icon = "" -wm_property = "class" split_at = ":" empty_label = "🌕" From ea1b13029723f12326e453f33041105f246da79d Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 11:37:52 +0100 Subject: [PATCH 38/45] style: rustfmt --- src/config.rs | 6 +++--- src/lib.rs | 56 +++++++++++++++++++++++++++------------------------ src/main.rs | 9 +++------ src/regex.rs | 16 +++++++-------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/src/config.rs b/src/config.rs index 4652f69..6749469 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,16 @@ use serde::Deserialize; use std::collections::HashMap; +use std::error::Error; use std::fs::File; use std::io::Read; use std::path::Path; -use std::error::Error; #[derive(Deserialize)] #[serde(default)] pub struct Aliases { pub class: HashMap, pub instance: HashMap, - pub name: HashMap + pub name: HashMap, } #[derive(Deserialize)] @@ -41,7 +41,7 @@ impl Default for Aliases { Aliases { class: HashMap::new(), instance: HashMap::new(), - name: HashMap::new() + name: HashMap::new(), } } } diff --git a/src/lib.rs b/src/lib.rs index cba5b42..4c7d08b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use i3ipc::{ event::{ inner::{WindowChange, WorkspaceChange}, @@ -8,6 +7,7 @@ use i3ipc::{ I3Connection, }; use itertools::Itertools; +use std::collections::HashMap; pub mod config; pub mod icons; @@ -15,7 +15,6 @@ pub mod regex; use config::Config; use std::error::Error; - /// Helper fn to get options via config fn get_option(config: &Config, key: &str) -> bool { return match config.options.get(key) { @@ -28,28 +27,41 @@ fn get_option(config: &Config, key: &str) -> bool { fn get_title( props: &HashMap, config: &Config, - res: ®ex::Compiled + res: ®ex::Compiled, ) -> Result> { - let wm_class = props.get(&WindowProperty::Class); let wm_instance = props.get(&WindowProperty::Instance); let wm_name = props.get(&WindowProperty::Title); // Check for aliases using pre-compiled regex let title = { - if let Some((_, alias)) = wm_name.and_then(|name| res.name.iter().filter(|(re, _)| re.is_match(&name)).next()) { + if let Some((_, alias)) = + wm_name.and_then(|name| res.name.iter().filter(|(re, _)| re.is_match(&name)).next()) + { alias - } else if let Some((_, alias)) = wm_instance.and_then(|instance| res.instance.iter().filter(|(re, _)| re.is_match(&instance)).next()) { + } else if let Some((_, alias)) = wm_instance.and_then(|instance| { + res.instance + .iter() + .filter(|(re, _)| re.is_match(&instance)) + .next() + }) { alias - } else if let Some((_, alias)) = wm_class.and_then(|class| res.class.iter().filter(|(re, _)| re.is_match(&class)).next()) { + } else if let Some((_, alias)) = wm_class.and_then(|class| { + res.class + .iter() + .filter(|(re, _)| re.is_match(&class)) + .next() + }) { alias } else { if let Some(class) = wm_class { class } else { - Err(format!("failed to get any class given these window properties {:#?}", props))? + Err(format!( + "failed to get any class given these window properties {:#?}", + props + ))? } - } }; @@ -108,7 +120,6 @@ fn get_workspaces(tree: Node) -> Vec { out } - /// get window ids for any depth collection of nodes fn get_properties(mut nodes: Vec>) -> Vec> { let mut window_props = Vec::new(); @@ -126,12 +137,7 @@ fn get_properties(mut nodes: Vec>) -> Vec Vec { - +fn collect_titles(workspace: &Node, config: &Config, res: ®ex::Compiled) -> Vec { let window_props = { let mut f = get_properties(vec![workspace.floating_nodes.iter().collect()]); let mut n = get_properties(vec![workspace.nodes.iter().collect()]); @@ -158,7 +164,7 @@ fn collect_titles( pub fn update_tree( i3_conn: &mut I3Connection, config: &Config, - res: ®ex::Compiled + res: ®ex::Compiled, ) -> Result<(), Box> { let tree = i3_conn.get_tree()?; for workspace in get_workspaces(tree) { @@ -194,7 +200,6 @@ pub fn update_tree( ) })?; - // Get split_at arg let split_at = match config.general.get("split_at") { Some(s) => { @@ -203,14 +208,14 @@ pub fn update_tree( } else { ' ' } - }, + } None => ' ', }; // Get the initial element we want to keep let initial = match old.split(split_at).next() { Some(i) => i, - None => "" + None => "", }; let mut new: String = String::from(initial); @@ -230,7 +235,7 @@ pub fn update_tree( new.push_str(" "); new.push_str(default_label); } - None => () + None => (), } } @@ -248,7 +253,7 @@ pub fn handle_window_event( e: &WindowEventInfo, i3_conn: &mut I3Connection, config: &Config, - res: ®ex::Compiled + res: ®ex::Compiled, ) -> Result<(), Box> { match e.change { WindowChange::New | WindowChange::Close | WindowChange::Move | WindowChange::Title => { @@ -264,7 +269,7 @@ pub fn handle_ws_event( e: &WorkspaceEventInfo, i3_conn: &mut I3Connection, config: &Config, - res: ®ex::Compiled + res: ®ex::Compiled, ) -> Result<(), Box> { match e.change { WorkspaceChange::Empty | WorkspaceChange::Focus => { @@ -278,9 +283,9 @@ pub fn handle_ws_event( #[cfg(test)] mod tests { use i3ipc::reply::{NodeType, WindowProperty}; - use std::error::Error; - use std::env; use std::collections::HashMap; + use std::env; + use std::error::Error; #[test] fn connection_tree() -> Result<(), Box> { @@ -359,7 +364,6 @@ mod tests { let workspaces = super::get_workspaces(tree); let mut result: Vec> = Vec::new(); for workspace in workspaces { - let window_props = { let mut f = super::get_properties(vec![workspace.floating_nodes.iter().collect()]); let mut n = super::get_properties(vec![workspace.nodes.iter().collect()]); diff --git a/src/main.rs b/src/main.rs index 9be9271..178ac82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,7 @@ struct Args { /// What character used to split the workspace title string #[arg(short = 'a', long)] - split_at: Option + split_at: Option, } /// Setup program by handling args and populating config @@ -137,15 +137,12 @@ fn main() -> Result<(), Box> { for event in listener.listen() { match event? { Event::WindowEvent(e) => { - if let Err(error) = - i3wsr::handle_window_event(&e, &mut i3_conn, &config, &res) - { + if let Err(error) = i3wsr::handle_window_event(&e, &mut i3_conn, &config, &res) { eprintln!("handle_window_event error: {}", error); } } Event::WorkspaceEvent(e) => { - if let Err(error) = i3wsr::handle_ws_event(&e, &mut i3_conn, &config, &res) - { + if let Err(error) = i3wsr::handle_ws_event(&e, &mut i3_conn, &config, &res) { eprintln!("handle_ws_event error: {}", error); } } diff --git a/src/regex.rs b/src/regex.rs index dbccad0..105ec99 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -4,9 +4,9 @@ use std::error::Error; pub type Point = (Regex, String); pub struct Compiled { - pub class: Vec, - pub instance: Vec, - pub name: Vec + pub class: Vec, + pub instance: Vec, + pub name: Vec, } fn compile((k, v): (&String, &String)) -> Result> { @@ -17,19 +17,19 @@ fn compile((k, v): (&String, &String)) -> Result> { pub fn parse_config(config: &Config) -> Result> { let classes = match config.aliases.class.iter().map(compile).collect() { Ok(v) => v, - Err(e) => Err(e)? + Err(e) => Err(e)?, }; let instances = match config.aliases.instance.iter().map(compile).collect() { Ok(v) => v, - Err(e) => Err(e)? + Err(e) => Err(e)?, }; let names = match config.aliases.name.iter().map(compile).collect() { Ok(v) => v, - Err(e) => Err(e)? + Err(e) => Err(e)?, }; return Ok(Compiled { class: classes, instance: instances, - name: names - }) + name: names, + }); } From 2e1d4a71ff324da7185f8afda82d641829dd5ff5 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 13:40:01 +0100 Subject: [PATCH 39/45] fix: handle no alias by adding display_prop conf After my last refactor of alias handling, there was one case I didn't consider: What happens if no alias is defined, but you still want to display a different window property than class as the title value?, and match icons on that. This fixes that by adding a new config value "display_property". It will be checked when no alias is located, and return a title based of that. Finally class is used, and if no properties are found it will return with error, reporting in stdout. --- src/lib.rs | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4c7d08b..eaa2532 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,17 @@ fn get_title( let wm_class = props.get(&WindowProperty::Class); let wm_instance = props.get(&WindowProperty::Instance); let wm_name = props.get(&WindowProperty::Title); + let display_prop = match config.general.get("display_property") { + Some(prop) => { + match prop.as_ref() { + "class" | "instance" | "name" => prop, + _ => "class" + } + }, + None => { + "class" + } + }; // Check for aliases using pre-compiled regex let title = { @@ -54,12 +65,18 @@ fn get_title( }) { alias } else { - if let Some(class) = wm_class { - class + // Handle display prop, if no alias is located, then check for existiance and + // display_prop to set a fallback title + if wm_name.is_some() && display_prop == "name" { + wm_name.unwrap() + } else if wm_instance.is_some() && display_prop == "instance" { + wm_instance.unwrap() + } else if wm_class.is_some() { + wm_class.unwrap() } else { Err(format!( - "failed to get any class given these window properties {:#?}", - props + "failed to get alias, display_prop {}, or class", + display_prop ))? } } @@ -150,7 +167,7 @@ fn collect_titles(workspace: &Node, config: &Config, res: ®ex::Compiled) -> V let title = match get_title(&props, config, res) { Ok(title) => title, Err(e) => { - eprintln!("get_title error: {}", e); + eprintln!("get_title error: \"{}\" for workspace {:#?}", e, workspace); continue; } }; From 37c4858c80e0fa70b513c598a2dbb788dbb0097f Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 13:56:36 +0100 Subject: [PATCH 40/45] docs: update readme and example config - Add display property option - Rewrite some explanations using new paradigme. - Update badge - Add new demo values to example config --- README.md | 51 +++++++++++++++++++++++++------------- assets/example_config.toml | 1 + 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index c7c3970..c11bea1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ i3wsr - i3 workspace renamer ====== -[![Build Status](https://app.travis-ci.com/roosta/i3wsr.svg?branch=main)](https://app.travis-ci.com/roosta/i3wsr) + +[![Test Status](https://github.com/roosta/i3wsr/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/roosta/i3wsr/actions) [![Crates.io](https://img.shields.io/crates/v/i3wsr)](https://crates.io/crates/i3wsr) `i3wsr` is a small program that uses [I3's](https://i3wm.org/) [IPC Interface](https://i3wm.org/docs/ipc.html) to change the name of a workspace based on its contents. -# TOC +## Table of content -- [i3wsr - i3 workspace renamer](#i3wsr---i3-workspace-renamer) -- [TOC](#toc) +- [Table of content](#table-of-content) - [Details](#details) - [Requirements](#requirements) - [Installation](#installation) @@ -19,13 +19,15 @@ to change the name of a workspace based on its contents. - [Keeping part of the workspace name](#keeping-part-of-the-workspace-name) - [Configuration / options](#configuration--options) - [Aliases](#aliases) - - [WM Property](#wm-property) + - [Display property](#display-property) + - [Aliases based on property](#aliases-based-on-property) - [Class](#class) - [Instance](#instance) - [Name](#name) - [Icons](#icons) - [Separator](#separator) - [Default icon](#default-icon) + - [Empty label](#empty-label) - [No icon names](#no-icon-names) - [No names](#no-names) - [Remove duplicates](#remove-duplicates) @@ -34,7 +36,6 @@ to change the name of a workspace based on its contents. - [Testing](#testing) - [Attribution](#attribution) - ## Details The chosen name for a workspace is a composite of the `WM_CLASS` X11 window @@ -78,7 +79,7 @@ exec_always --no-startup-id $HOME/.cargo/bin/i3wsr exec_always --no-startup-id /usr/bin/i3wsr ``` -## i3-configuration +## i3 configuration This program depends on numbered workspaces, since we're constantly changing the workspace name. So your I3 configuration need to reflect this: @@ -125,8 +126,11 @@ Example config can be found in ### Aliases -Sometimes a WM property can be overly verbose, so its possible to match a -class name with an alias: + +Sometimes a class, instance or name can be overly verbose, use aliases that +match to window properties to create simpler names instead of showing the full +property + ```toml [aliases.class] @@ -150,21 +154,33 @@ Alias keys uses regex for matching, so it's possible to get creative: Remember to quote anything but `[a-zA-Z]`, and to escape your slashes. Due to rust string escapes if you want a literal backslash use two slashes `\\d`. -### WM Property +### Aliases based on property i3wsr supports 3 window properties currently: ```toml -[aliases.class] -[aliases.instance] -[aliases.name] +[aliases.name] // 1 +[aliases.instance] // 2 +[aliases.class] // 3 ``` -These are checked in decending order, so if i3wsr finds a name alias, it'll use -that and if not, then check instance, then finally use class +These are checked in descending order, so if i3wsr finds a name alias, it'll +use that and if not, then check instance, then finally use class > Deprecation note: previously `wm_property` defined which prop to check for > aliases, but this newer approach will allow for multiple types of aliases +### Display property + +Which property to display if no aliases if found: + +```toml +[general] +display_property = "instance" +``` + +Possible options are `class`, `instance`, and `name`, and will default to `class` +if not present. + #### Class This is the default, and the most succinct. @@ -228,8 +244,9 @@ Firefox = "🌍" # Use quote when matching anything other than [a-zA-Z] "Org.gnome.Nautilus" = "📘" ``` -i3wsr tries to match an icon with an alias first, then falls back to window -class, so a config like this is valid: +i3wsr tries to match an icon with an alias first, if none are found it then +checks your `display_property`, and tries to match an icon with a non aliased +`display_property`, lastly it will try to match on class. ```toml [aliases.class] diff --git a/assets/example_config.toml b/assets/example_config.toml index 8e72466..3f00ef0 100644 --- a/assets/example_config.toml +++ b/assets/example_config.toml @@ -24,6 +24,7 @@ separator = "  " default_icon = "" split_at = ":" empty_label = "🌕" +display_property = "instance" # class, instance, name [options] remove_duplicates = false From 3e74e5f6cc21dff64224e22df0ac6594e2225ed8 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 14:11:30 +0100 Subject: [PATCH 41/45] docs: Fix badge, update toc, fix section placement --- README.md | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c11bea1..4717663 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,17 @@ i3wsr - i3 workspace renamer ====== -[![Test Status](https://github.com/roosta/i3wsr/actions/workflows/test.yml/badge.svg?branch=develop)](https://github.com/roosta/i3wsr/actions) +[![Test Status](https://github.com/roosta/i3wsr/actions/workflows/test.yaml/badge.svg?branch=develop)](https://github.com/roosta/i3wsr/actions) [![Crates.io](https://img.shields.io/crates/v/i3wsr)](https://crates.io/crates/i3wsr) + `i3wsr` is a small program that uses [I3's](https://i3wm.org/) [IPC Interface](https://i3wm.org/docs/ipc.html) to change the name of a workspace based on its contents. -## Table of content +## TOC -- [Table of content](#table-of-content) +- [i3wsr - i3 workspace renamer](#i3wsr---i3-workspace-renamer) +- [TOC](#toc) - [Details](#details) - [Requirements](#requirements) - [Installation](#installation) @@ -19,11 +21,11 @@ to change the name of a workspace based on its contents. - [Keeping part of the workspace name](#keeping-part-of-the-workspace-name) - [Configuration / options](#configuration--options) - [Aliases](#aliases) - - [Display property](#display-property) - [Aliases based on property](#aliases-based-on-property) - [Class](#class) - [Instance](#instance) - [Name](#name) + - [Display property](#display-property) - [Icons](#icons) - [Separator](#separator) - [Default icon](#default-icon) @@ -169,18 +171,6 @@ use that and if not, then check instance, then finally use class > Deprecation note: previously `wm_property` defined which prop to check for > aliases, but this newer approach will allow for multiple types of aliases -### Display property - -Which property to display if no aliases if found: - -```toml -[general] -display_property = "instance" -``` - -Possible options are `class`, `instance`, and `name`, and will default to `class` -if not present. - #### Class This is the default, and the most succinct. @@ -229,6 +219,18 @@ It should be possible to write a launcher script, that wraps whatever command your running with a custom i3 ipc trigger event. If anyone figures out a nice way of doing it let me know. +### Display property + +Which property to display if no aliases if found: + +```toml +[general] +display_property = "instance" +``` + +Possible options are `class`, `instance`, and `name`, and will default to `class` +if not present. + ### Icons You can configure icons for your WM property, a very basic preset for From 8e3d8880fa3d262356a4797a69827c07bd2e9feb Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 14:15:28 +0100 Subject: [PATCH 42/45] docs: fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4717663..eb88103 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ a nice way of doing it let me know. ### Display property -Which property to display if no aliases if found: +Which property to display if no aliases is found: ```toml [general] From 9cdff81a0de3cbdd3a5cf81c6711e1f826a4915c Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 14:20:57 +0100 Subject: [PATCH 43/45] ci: remove old travis conf --- .travis.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e945fb2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -dist: xenial -services: - - xvfb -before_script: - - echo "deb http://cz.archive.ubuntu.com/ubuntu xenial main universe" | sudo tee -a /etc/apt/sources.list - - sudo apt-get update -qq - - sudo apt-get install i3-wm gcc gpick xterm -y - - "export DISPLAY=:99.0" - # - "sh -e /etc/init.d/xvfb start" - # - "sleep 3" - - "i3 -c /dev/null &" - - "gpick &" - - "sleep 3" - - "xterm &" - - "sleep 3" - - "DISPLAY=:99.0 i3-msg [class=\"XTerm\"] floating enable" -language: rust -sudo: true -cache: cargo -rust: - - stable - - beta - - nightly -matrix: - allow_failures: - - rust: nightly From c48394bc3b932d4d766002117237893d52879c26 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sat, 11 Nov 2023 14:31:14 +0100 Subject: [PATCH 44/45] chore: remove leftover cmd opt --- src/lib.rs | 1 - src/main.rs | 23 ----------------------- 2 files changed, 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eaa2532..b5f5533 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ fn get_option(config: &Config, key: &str) -> bool { }; } -/// Gets a window title, depends on wm_property config opt fn get_title( props: &HashMap, config: &Config, diff --git a/src/main.rs b/src/main.rs index 178ac82..9db27ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,6 @@ enum Icons { Awesome, } -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] -enum Properties { - Class, - Instance, - Name, -} - /// i3wsr config #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -41,10 +34,6 @@ struct Args { #[arg(short, long)] remove_duplicates: bool, - /// Which window property to use - #[arg(short = 'p', long)] - wm_property: Option, - /// What character used to split the workspace title string #[arg(short = 'a', long)] split_at: Option, @@ -106,18 +95,6 @@ fn setup() -> Result> { config.general.insert("split_at".to_string(), split_char); } - // wm property - let wm_property = match args.wm_property { - Some(prop) => match prop { - Properties::Class => String::from("class"), - Properties::Instance => String::from("instance"), - Properties::Name => String::from("name"), - }, - None => String::from("class"), - }; - config - .general - .insert("wm_property".to_string(), wm_property); Ok(config) } From 5f169e6a1ec153e8a27bb82bfb6dc5dc989a9097 Mon Sep 17 00:00:00 2001 From: Daniel Berg Date: Sun, 12 Nov 2023 14:18:01 +0100 Subject: [PATCH 45/45] fix: add display property as a cmd opt Removed this previously because wm_property was deprecated, but I should've instead changed it to display_property --- README.md | 5 +++++ src/lib.rs | 11 ++--------- src/main.rs | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eb88103..78ef714 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,11 @@ display_property = "instance" Possible options are `class`, `instance`, and `name`, and will default to `class` if not present. +You can alternatively supply cmd argument: + +```sh +i3wsr --display-property instance +``` ### Icons You can configure icons for your WM property, a very basic preset for diff --git a/src/lib.rs b/src/lib.rs index b5f5533..d836967 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,15 +32,8 @@ fn get_title( let wm_instance = props.get(&WindowProperty::Instance); let wm_name = props.get(&WindowProperty::Title); let display_prop = match config.general.get("display_property") { - Some(prop) => { - match prop.as_ref() { - "class" | "instance" | "name" => prop, - _ => "class" - } - }, - None => { - "class" - } + Some(prop) => prop, + None => "class", }; // Check for aliases using pre-compiled regex diff --git a/src/main.rs b/src/main.rs index 9db27ca..332c138 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,13 @@ enum Icons { Awesome, } +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)] +enum Properties { + Class, + Instance, + Name, +} + /// i3wsr config #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] @@ -34,6 +41,10 @@ struct Args { #[arg(short, long)] remove_duplicates: bool, + /// Which window property to use when no alias is found + #[arg(short = 'p', long)] + display_property: Option, + /// What character used to split the workspace title string #[arg(short = 'a', long)] split_at: Option, @@ -95,6 +106,18 @@ fn setup() -> Result> { config.general.insert("split_at".to_string(), split_char); } + // wm property + let display_property = match args.display_property { + Some(prop) => match prop { + Properties::Class => String::from("class"), + Properties::Instance => String::from("instance"), + Properties::Name => String::from("name"), + }, + None => String::from("class"), + }; + config + .general + .insert("display_property".to_string(), display_property); Ok(config) }