Compare commits

..

1 Commits

Author SHA1 Message Date
LGUG2Z
0f6d1a465c refactor(wm): use notopmost flag instead of bottom 2024-03-09 07:16:07 -08:00
27 changed files with 804 additions and 1333 deletions

View File

@@ -121,7 +121,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SCOOP_TOKEN: ${{ secrets.SCOOP_TOKEN }}
- name: Add MSI to release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/v')
with:
files: "target/wix/komorebi-*.msi"

3
.gitignore vendored
View File

@@ -3,5 +3,4 @@
/target
CHANGELOG.md
dummy.go
komorebi.ahk
komorebic/applications.yaml
komorebi.ahk

264
Cargo.lock generated
View File

@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.3"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
@@ -82,15 +82,15 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.2.0"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.71"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
@@ -124,27 +124,27 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bumpalo"
version = "3.15.4"
version = "3.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
[[package]]
name = "bytes"
version = "1.6.0"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.90"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
[[package]]
name = "cfg-if"
@@ -158,17 +158,11 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "clap"
version = "4.5.4"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
dependencies = [
"clap_builder",
"clap_derive",
@@ -176,9 +170,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.2"
version = "4.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
dependencies = [
"anstream",
"anstyle",
@@ -189,14 +183,14 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.5.4"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck 0.5.0",
"heck",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -207,9 +201,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "color-eyre"
version = "0.6.3"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
dependencies = [
"backtrace",
"color-spantrace",
@@ -290,9 +284,9 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "ctrlc"
version = "3.4.4"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
dependencies = [
"nix",
"windows-sys 0.52.0",
@@ -392,9 +386,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "2.0.2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "file-id"
@@ -550,9 +544,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "h2"
version = "0.3.25"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb"
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
dependencies = [
"bytes",
"fnv",
@@ -580,10 +574,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hex_color"
@@ -618,9 +612,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.12"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
dependencies = [
"bytes",
"fnv",
@@ -705,9 +699,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "2.2.6"
version = "2.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4"
dependencies = [
"equivalent",
"hashbrown",
@@ -753,18 +747,18 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "js-sys"
version = "0.3.69"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "komorebi"
version = "0.1.23-dev.0"
version = "0.1.22"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"clap",
"color-eyre",
"crossbeam-channel",
@@ -803,7 +797,7 @@ dependencies = [
[[package]]
name = "komorebi-client"
version = "0.1.23-dev.0"
version = "0.1.22"
dependencies = [
"komorebi",
"komorebi-core",
@@ -813,7 +807,7 @@ dependencies = [
[[package]]
name = "komorebi-core"
version = "0.1.23-dev.0"
version = "0.1.22"
dependencies = [
"clap",
"color-eyre",
@@ -829,7 +823,7 @@ dependencies = [
[[package]]
name = "komorebic"
version = "0.1.23-dev.0"
version = "0.1.22"
dependencies = [
"clap",
"color-eyre",
@@ -837,7 +831,7 @@ dependencies = [
"dirs",
"dunce",
"fs-tail",
"heck 0.5.0",
"heck",
"komorebi-core",
"lazy_static",
"miette",
@@ -856,7 +850,7 @@ dependencies = [
[[package]]
name = "komorebic-no-console"
version = "0.1.23-dev.0"
version = "0.1.22"
[[package]]
name = "kqueue"
@@ -896,7 +890,7 @@ version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"libc",
"redox_syscall",
]
@@ -949,13 +943,12 @@ dependencies = [
[[package]]
name = "miette"
version = "7.2.0"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4edc8853320c2a0dab800fbda86253c8938f6ea88510dc92c5f1ed20e794afc1"
checksum = "baed61d13cc3723ee6dbed730a82bfacedc60a85d81da2d77e9c3e8ebc0b504a"
dependencies = [
"backtrace",
"backtrace-ext",
"cfg-if 1.0.0",
"miette-derive",
"owo-colors 4.0.0",
"supports-color",
@@ -969,13 +962,13 @@ dependencies = [
[[package]]
name = "miette-derive"
version = "7.2.0"
version = "7.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf09caffaac8068c346b6df2a7fc27a177fd20b39421a39ce0a211bde679a6c"
checksum = "f301c3f54f98abc6c212ee722f5e5c62e472a334415840669e356f04850051ec"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -1054,13 +1047,12 @@ dependencies = [
[[package]]
name = "nix"
version = "0.28.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"cfg-if 1.0.0",
"cfg_aliases",
"libc",
]
@@ -1070,7 +1062,7 @@ version = "6.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"crossbeam-channel",
"filetime",
"fsevent-sys",
@@ -1121,6 +1113,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "object"
version = "0.32.2"
@@ -1142,7 +1144,7 @@ version = "0.10.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"cfg-if 1.0.0",
"foreign-types",
"libc",
@@ -1159,7 +1161,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -1188,13 +1190,13 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "os_info"
version = "3.8.2"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e"
dependencies = [
"log",
"serde",
"windows-sys 0.52.0",
"winapi",
]
[[package]]
@@ -1325,9 +1327,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
dependencies = [
"unicode-ident",
]
@@ -1373,9 +1375,9 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.10.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd"
dependencies = [
"either",
"rayon-core",
@@ -1413,13 +1415,13 @@ dependencies = [
[[package]]
name = "regex"
version = "1.10.4"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.6",
"regex-automata 0.4.5",
"regex-syntax 0.8.2",
]
@@ -1434,9 +1436,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.6"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
dependencies = [
"aho-corasick",
"memchr",
@@ -1457,9 +1459,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwest"
version = "0.11.27"
version = "0.11.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
dependencies = [
"base64",
"bytes",
@@ -1503,11 +1505,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.38.32"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags 2.5.0",
"bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys",
@@ -1623,7 +1625,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -1673,9 +1675,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
version = "0.9.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f"
dependencies = [
"indexmap",
"itoa",
@@ -1704,9 +1706,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.13.2"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "smawk"
@@ -1732,24 +1734,24 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "strum"
version = "0.26.2"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.26.2"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946"
checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18"
dependencies = [
"heck 0.4.1",
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -1786,9 +1788,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.55"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
@@ -1803,9 +1805,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "sysinfo"
version = "0.30.7"
version = "0.30.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c385888ef380a852a16209afc8cfad22795dd8873d69c9a14d2e2088f118d18"
checksum = "6746919caf9f2a85bff759535664c060109f21975c5ac2e8652e60102bd4d196"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
@@ -1872,22 +1874,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.58"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -1966,6 +1968,7 @@ dependencies = [
"bytes",
"libc",
"mio",
"num_cpus",
"pin-project-lite",
"socket2",
"windows-sys 0.48.0",
@@ -2032,7 +2035,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -2136,9 +2139,9 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
[[package]]
name = "url"
@@ -2202,9 +2205,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@@ -2212,24 +2215,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.42"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
@@ -2239,9 +2242,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2249,28 +2252,28 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.92"
version = "0.2.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
[[package]]
name = "web-sys"
version = "0.3.69"
version = "0.3.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -2278,14 +2281,15 @@ dependencies = [
[[package]]
name = "which"
version = "6.0.1"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7"
checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
"winsafe",
"windows-sys 0.52.0",
]
[[package]]
@@ -2374,7 +2378,7 @@ checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -2385,7 +2389,7 @@ checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.52",
]
[[package]]
@@ -2614,9 +2618,3 @@ dependencies = [
"cfg-if 1.0.0",
"windows-sys 0.48.0",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-client"
version = "0.1.23-dev.0"
version = "0.1.22"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.23-dev.0"
version = "0.1.22"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -8,9 +8,7 @@ use strum::EnumString;
use crate::ApplicationIdentifier;
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ApplicationOptions {
@@ -52,13 +50,6 @@ impl ApplicationOptions {
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum MatchingRule {
Simple(IdWithIdentifier),
Composite(Vec<IdWithIdentifier>),
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifier {
pub kind: ApplicationIdentifier,
@@ -75,10 +66,6 @@ pub enum MatchingStrategy {
EndsWith,
Contains,
Regex,
DoesNotEndWith,
DoesNotStartWith,
DoesNotEqual,
DoesNotContain,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
@@ -108,7 +95,7 @@ pub struct ApplicationConfiguration {
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<Vec<ApplicationOptions>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub float_identifiers: Option<Vec<MatchingRule>>,
pub float_identifiers: Option<Vec<IdWithIdentifierAndComment>>,
}
impl ApplicationConfiguration {
@@ -194,21 +181,19 @@ impl ApplicationConfigurationGenerator {
}
if let Some(float_identifiers) = app.float_identifiers {
for matching_rule in float_identifiers {
if let MatchingRule::Simple(float) = matching_rule {
let float_rule =
format!("komorebic.exe float-rule {} \"{}\"", float.kind, float.id);
for float in float_identifiers {
let float_rule =
format!("komorebic.exe float-rule {} \"{}\"", float.kind, float.id);
// Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone());
// Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone());
// if let Some(comment) = float.comment {
// lines.push(format!("# {comment}"));
// };
if let Some(comment) = float.comment {
lines.push(format!("# {comment}"));
};
lines.push(float_rule);
}
lines.push(float_rule);
}
}
}
@@ -245,23 +230,21 @@ impl ApplicationConfigurationGenerator {
}
if let Some(float_identifiers) = app.float_identifiers {
for matching_rule in float_identifiers {
if let MatchingRule::Simple(float) = matching_rule {
let float_rule = format!(
"RunWait('komorebic.exe float-rule {} \"{}\"', , \"Hide\")",
float.kind, float.id
);
for float in float_identifiers {
let float_rule = format!(
"RunWait('komorebic.exe float-rule {} \"{}\"', , \"Hide\")",
float.kind, float.id
);
// Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone());
// Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone());
// if let Some(comment) = float.comment {
// lines.push(format!("; {comment}"));
// };
if let Some(comment) = float.comment {
lines.push(format!("; {comment}"));
};
lines.push(float_rule);
}
lines.push(float_rule);
}
}
}

View File

@@ -26,29 +26,13 @@ impl From<RECT> for Rect {
}
}
impl From<Rect> for RECT {
fn from(rect: Rect) -> Self {
Self {
left: rect.left,
top: rect.top,
right: rect.right,
bottom: rect.bottom,
}
}
}
impl Rect {
/// decrease the size of self by the padding amount.
pub fn add_padding<T>(&mut self, padding: T)
where
T: Into<Option<i32>>,
{
if let Some(padding) = padding.into() {
self.left += padding;
self.top += padding;
self.right -= padding * 2;
self.bottom -= padding * 2;
}
pub fn add_padding(&mut self, padding: i32) {
self.left += padding;
self.top += padding;
self.right -= padding * 2;
self.bottom -= padding * 2;
}
/// increase the size of self by the margin amount.
@@ -59,14 +43,6 @@ impl Rect {
self.bottom += margin * 2;
}
pub fn left_padding(&mut self, padding: i32) {
self.left += padding;
}
pub fn right_padding(&mut self, padding: i32) {
self.right -= padding;
}
#[must_use]
pub const fn contains_point(&self, point: (i32, i32)) -> bool {
point.0 >= self.left

27
komorebi.example.json Normal file
View File

@@ -0,0 +1,27 @@
{
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.22/schema.json",
"app_specific_configuration_path": "$Env:USERPROFILE/applications.yaml",
"window_hiding_behaviour": "Cloak",
"cross_monitor_move_behaviour": "Insert",
"default_workspace_padding": 20,
"default_container_padding": 20,
"border_padding": 8,
"border_offset": -1,
"active_window_border": false,
"active_window_border_colours": {
"single": "#42a5f5",
"stack": "#00a542",
"monocle": "#ff3399"
},
"monitors": [
{
"workspaces": [
{ "name": "I", "layout": "BSP" },
{ "name": "II", "layout": "VerticalStack" },
{ "name": "III", "layout": "HorizontalStack" },
{ "name": "IV", "layout": "UltrawideVerticalStack" },
{ "name": "V", "layout": "Rows" }
]
}
]
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.23-dev.0"
version = "0.1.22"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -27,7 +27,7 @@ lazy_static = "1"
miow = "0.5"
nanoid = "0.4"
net2 = "0.2"
os_info = "3.8"
os_info = "3.7"
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
paste = "1"
regex = "1"

View File

@@ -7,18 +7,13 @@ use serde::Deserialize;
use serde::Serialize;
use crate::ring::Ring;
use crate::stackbar::Stackbar;
use crate::window::Window;
use crate::StackbarMode;
use crate::STACKBAR_MODE;
#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
pub struct Container {
#[getset(get = "pub")]
id: String,
windows: Ring<Window>,
#[getset(get = "pub", get_mut = "pub")]
stackbar: Option<Stackbar>,
}
impl_ring_elements!(Container, Window);
@@ -28,10 +23,6 @@ impl Default for Container {
Self {
id: nanoid!(),
windows: Ring::default(),
stackbar: match *STACKBAR_MODE.lock() {
StackbarMode::Always => Stackbar::create().ok(),
StackbarMode::Never | StackbarMode::OnStack => None,
},
}
}
}
@@ -43,38 +34,6 @@ impl PartialEq for Container {
}
impl Container {
pub fn hide(&self, omit: Option<isize>) {
if let Some(stackbar) = self.stackbar() {
stackbar.hide();
}
for window in self.windows().iter().rev() {
let mut should_hide = omit.is_none();
if !should_hide {
if let Some(omit) = omit {
if omit != window.hwnd {
should_hide = true
}
}
}
if should_hide {
window.hide();
}
}
}
pub fn restore(&self) {
if let Some(stackbar) = self.stackbar() {
stackbar.restore();
}
if let Some(window) = self.focused_window() {
window.restore();
}
}
pub fn load_focused_window(&mut self) {
let focused_idx = self.focused_window_idx();
for (i, window) in self.windows_mut().iter_mut().enumerate() {
@@ -122,10 +81,6 @@ impl Container {
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
let window = self.windows_mut().remove(idx);
if matches!(*STACKBAR_MODE.lock(), StackbarMode::OnStack) && self.windows().len() <= 1 {
self.stackbar = None;
}
if idx != 0 {
self.focus_window(idx - 1);
};
@@ -140,14 +95,6 @@ impl Container {
pub fn add_window(&mut self, window: Window) {
self.windows_mut().push_back(window);
if matches!(*STACKBAR_MODE.lock(), StackbarMode::OnStack)
&& self.windows().len() > 1
&& self.stackbar.is_none()
{
self.stackbar = Stackbar::create().ok();
}
self.focus_window(self.windows().len() - 1);
}

View File

@@ -10,7 +10,6 @@ pub mod process_command;
pub mod process_event;
pub mod process_movement;
pub mod set_window_position;
pub mod stackbar;
pub mod static_config;
pub mod styles;
pub mod window;
@@ -24,7 +23,6 @@ pub mod workspace;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::fs::File;
use std::io::Write;
use std::net::TcpStream;
@@ -40,7 +38,6 @@ use std::sync::Arc;
pub use hidden::*;
pub use process_command::*;
pub use process_event::*;
pub use stackbar::*;
pub use static_config::*;
pub use window_manager::*;
pub use window_manager_event::*;
@@ -49,7 +46,6 @@ pub use windows_api::*;
use color_eyre::Result;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
@@ -70,57 +66,57 @@ type WorkspaceRule = (usize, usize, bool);
lazy_static! {
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
static ref LAYERED_WHITELIST: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
MatchingRule::Simple(IdWithIdentifier {
static ref LAYERED_WHITELIST: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
},
]));
static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> =
static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> =
Arc::new(Mutex::new(vec![
MatchingRule::Simple(IdWithIdentifier {
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("explorer.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("chrome.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("ApplicationFrameHost.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
})
}
]));
static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
MatchingRule::Simple(IdWithIdentifier {
static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
},
]));
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
Arc::new(Mutex::new(HashMap::new()));
@@ -130,26 +126,25 @@ lazy_static! {
Arc::new(Mutex::new(HashMap::new()));
static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
// mstsc.exe creates these on Windows 11 when a WSL process is launched
// https://github.com/LGUG2Z/komorebi/issues/74
MatchingRule::Simple(IdWithIdentifier {
IdWithIdentifier {
kind: ApplicationIdentifier::Class,
id: String::from("OPContainerClass"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}),
MatchingRule::Simple(IdWithIdentifier {
},
IdWithIdentifier {
kind: ApplicationIdentifier::Class,
id: String::from("IHWindowClass"),
matching_strategy: Option::from(MatchingStrategy::Equals),
})
}
]));
static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(),
]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(),
"vcxsrv.exe".to_string(),
@@ -194,9 +189,6 @@ lazy_static! {
)
};
static ref ACTIVE_WINDOW_BORDER_STYLE: Arc<Mutex<ActiveWindowBorderStyle>> =
Arc::new(Mutex::new(ActiveWindowBorderStyle::System));
static ref BORDER_RECT: Arc<Mutex<Rect>> =
Arc::new(Mutex::new(Rect::default()));
@@ -204,11 +196,6 @@ lazy_static! {
// Use app-specific titlebar removal options where possible
// eg. Windows Terminal, IntelliJ IDEA, Firefox
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref STACKBAR_MODE: Arc<Mutex<StackbarMode >> = Arc::new(Mutex::new(StackbarMode::Never));
static ref WINDOWS_BY_BAR_HWNDS: Arc<Mutex<HashMap<isize, VecDeque<isize>>>> =
Arc::new(Mutex::new(HashMap::new()));
}
pub static DEFAULT_WORKSPACE_PADDING: AtomicI32 = AtomicI32::new(10);
@@ -233,12 +220,6 @@ pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
#[must_use]
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);

View File

@@ -71,7 +71,7 @@ impl Monitor {
if i == focused_idx {
workspace.restore(mouse_follows_focus)?;
} else {
workspace.hide(None);
workspace.hide();
}
}

View File

@@ -23,7 +23,6 @@ use uds_windows::UnixStream;
use komorebi_core::config_generation::ApplicationConfiguration;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis;
@@ -217,7 +216,6 @@ impl WindowManager {
SocketMessage::UnstackWindow => self.remove_window_from_container()?,
SocketMessage::CycleStack(direction) => {
self.cycle_container_window_in_direction(direction)?;
self.focused_window()?.focus(self.mouse_follows_focus)?;
}
SocketMessage::ForceFocus => {
let focused_window = self.focused_window()?;
@@ -275,19 +273,17 @@ impl WindowManager {
let mut should_push = true;
for m in &*manage_identifiers {
if let MatchingRule::Simple(m) = m {
if m.id.eq(id) {
should_push = false;
}
if m.id.eq(id) {
should_push = false;
}
}
if should_push {
manage_identifiers.push(MatchingRule::Simple(IdWithIdentifier {
manage_identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
}
SocketMessage::FloatRule(identifier, ref id) => {
@@ -295,19 +291,17 @@ impl WindowManager {
let mut should_push = true;
for f in &*float_identifiers {
if let MatchingRule::Simple(f) = f {
if f.id.eq(id) {
should_push = false;
}
if f.id.eq(id) {
should_push = false;
}
}
if should_push {
float_identifiers.push(MatchingRule::Simple(IdWithIdentifier {
float_identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
let offset = self.work_area_offset;
@@ -1031,19 +1025,17 @@ impl WindowManager {
let mut should_push = true;
for i in &*identifiers {
if let MatchingRule::Simple(i) = i {
if i.id.eq(id) {
should_push = false;
}
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(MatchingRule::Simple(IdWithIdentifier {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
}
SocketMessage::IdentifyObjectNameChangeApplication(identifier, ref id) => {
@@ -1051,38 +1043,34 @@ impl WindowManager {
let mut should_push = true;
for i in &*identifiers {
if let MatchingRule::Simple(i) = i {
if i.id.eq(id) {
should_push = false;
}
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(MatchingRule::Simple(IdWithIdentifier {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
}
SocketMessage::IdentifyTrayApplication(identifier, ref id) => {
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
let mut should_push = true;
for i in &*identifiers {
if let MatchingRule::Simple(i) = i {
if i.id.eq(id) {
should_push = false;
}
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(MatchingRule::Simple(IdWithIdentifier {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
}
SocketMessage::IdentifyLayeredApplication(identifier, ref id) => {
@@ -1090,19 +1078,17 @@ impl WindowManager {
let mut should_push = true;
for i in &*identifiers {
if let MatchingRule::Simple(i) = i {
if i.id.eq(id) {
should_push = false;
}
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(MatchingRule::Simple(IdWithIdentifier {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
}));
});
}
}
SocketMessage::ManageFocusedWindow => {

View File

@@ -147,7 +147,7 @@ impl WindowManager {
match event {
WindowManagerEvent::Raise(window) => {
window.focus(false)?;
window.raise();
self.has_pending_raise_op = false;
}
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
@@ -518,9 +518,6 @@ impl WindowManager {
}
}
}
WindowManagerEvent::ForceUpdate(_) => {
self.update_focused_workspace(false)?;
}
WindowManagerEvent::DisplayChange(..)
| WindowManagerEvent::MouseCapture(..)
| WindowManagerEvent::Cloak(..)

View File

@@ -1,294 +0,0 @@
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::eyre::Result;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use windows::core::PCWSTR;
use windows::Win32::Foundation::COLORREF;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
use windows::Win32::Graphics::Gdi::DrawTextW;
use windows::Win32::Graphics::Gdi::GetDC;
use windows::Win32::Graphics::Gdi::ReleaseDC;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::SetBkColor;
use windows::Win32::Graphics::Gdi::SetTextColor;
use windows::Win32::Graphics::Gdi::DT_CENTER;
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
use windows::Win32::Graphics::Gdi::DT_VCENTER;
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
use windows::Win32::Graphics::Gdi::FW_BOLD;
use windows::Win32::Graphics::Gdi::LOGFONTW;
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::SW_SHOW;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
use komorebi_core::Rect;
use crate::window::Window;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::winevent_listener;
use crate::WindowManagerEvent;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
use crate::STACKBAR_TAB_HEIGHT;
use crate::STACKBAR_TAB_WIDTH;
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_BY_BAR_HWNDS;
#[derive(Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct Stackbar {
pub(crate) hwnd: isize,
#[serde(skip)]
pub is_cloned: bool,
}
impl Drop for Stackbar {
fn drop(&mut self) {
if !self.is_cloned {
let _ = WindowsApi::close_window(self.hwnd());
}
}
}
impl Clone for Stackbar {
fn clone(&self) -> Self {
Self {
hwnd: self.hwnd,
is_cloned: true,
}
}
}
impl Stackbar {
unsafe extern "system" fn window_proc(
hwnd: HWND,
msg: u32,
w_param: WPARAM,
l_param: LPARAM,
) -> LRESULT {
match msg {
WM_LBUTTONDOWN => {
let win_hwnds_by_topbar = WINDOWS_BY_BAR_HWNDS.lock();
if let Some(win_hwnds) = win_hwnds_by_topbar.get(&hwnd.0) {
let x = l_param.0 as i32 & 0xFFFF;
let y = (l_param.0 as i32 >> 16) & 0xFFFF;
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
for (index, win_hwnd) in win_hwnds.iter().enumerate() {
let left = gap + (index as i32 * (width + gap));
let right = left + width;
let top = 0;
let bottom = height;
if x >= left && x <= right && y >= top && y <= bottom {
let window = Window { hwnd: *win_hwnd };
let event_sender = winevent_listener::event_tx();
let _ = event_sender.send(WindowManagerEvent::FocusChange(
WinEvent::ObjectFocus,
window,
));
let _ = event_sender.send(WindowManagerEvent::ForceUpdate(window));
}
}
}
WINDOWS_BY_BAR_HWNDS.force_unlock();
LRESULT(0)
}
WM_DESTROY => {
PostQuitMessage(0);
LRESULT(0)
}
_ => DefWindowProcW(hwnd, msg, w_param, l_param),
}
}
pub const fn hwnd(&self) -> HWND {
HWND(self.hwnd)
}
pub fn create() -> Result<Stackbar> {
let name: Vec<u16> = "komorebi_stackbar\0".encode_utf16().collect();
let class_name = PCWSTR(name.as_ptr());
let h_module = WindowsApi::module_handle_w()?;
let wnd_class = WNDCLASSW {
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(Self::window_proc),
hInstance: h_module.into(),
lpszClassName: class_name,
hbrBackground: WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR),
..Default::default()
};
unsafe {
RegisterClassW(&wnd_class);
}
let (hwnd_sender, hwnd_receiver) = crossbeam_channel::bounded::<HWND>(1);
let name_cl = name.clone();
std::thread::spawn(move || -> Result<()> {
unsafe {
let hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
PCWSTR(name_cl.as_ptr()),
PCWSTR(name_cl.as_ptr()),
WS_POPUP | WS_VISIBLE,
0,
0,
0,
0,
None,
None,
h_module,
None,
);
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY)?;
hwnd_sender.send(hwnd)?;
let mut msg = MSG::default();
while GetMessageW(&mut msg, hwnd, 0, 0).into() {
TranslateMessage(&msg);
DispatchMessageW(&msg);
std::thread::sleep(Duration::from_millis(10));
}
}
Ok(())
});
Ok(Self {
hwnd: hwnd_receiver.recv()?.0,
..Default::default()
})
}
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
WindowsApi::position_window(self.hwnd(), layout, top)
}
pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect {
Rect {
bottom: STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst),
..*layout
}
}
pub fn update(&self, windows: &VecDeque<Window>, focused_hwnd: isize) -> Result<()> {
let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst);
let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
let background = STACKBAR_TAB_BACKGROUND_COLOUR.load(Ordering::SeqCst);
let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load(Ordering::SeqCst);
unsafe {
let hdc = GetDC(self.hwnd());
let hpen = CreatePen(PS_SOLID, 0, COLORREF(background));
let hbrush = CreateSolidBrush(COLORREF(background));
SelectObject(hdc, hpen);
SelectObject(hdc, hbrush);
SetBkColor(hdc, COLORREF(background));
let hfont = CreateFontIndirectW(&LOGFONTW {
lfWeight: FW_BOLD.0 as i32,
lfQuality: FONT_QUALITY(PROOF_QUALITY.0),
..Default::default()
});
SelectObject(hdc, hfont);
for (i, window) in windows.iter().enumerate() {
if window.hwnd == focused_hwnd {
SetTextColor(hdc, COLORREF(focused_text_colour));
window.focus(false)?;
} else {
SetTextColor(hdc, COLORREF(unfocused_text_colour));
}
let left = gap + (i as i32 * (width + gap));
let mut tab_box = Rect {
top: 0,
left,
right: left + width,
bottom: height,
};
WindowsApi::round_rect(hdc, &tab_box, 8);
let exe = window.exe()?;
let exe_trimmed = exe.trim_end_matches(".exe");
let mut tab_title: Vec<u16> = exe_trimmed.encode_utf16().collect();
tab_box.left_padding(10);
tab_box.right_padding(10);
DrawTextW(
hdc,
&mut tab_title,
&mut tab_box.into(),
DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS,
);
}
ReleaseDC(self.hwnd(), hdc);
}
let mut windows_hwdns: VecDeque<isize> = VecDeque::new();
for window in windows {
windows_hwdns.push_back(window.hwnd);
}
WINDOWS_BY_BAR_HWNDS.lock().insert(self.hwnd, windows_hwdns);
Ok(())
}
pub fn hide(&self) {
WindowsApi::hide_window(self.hwnd())
}
pub fn restore(&self) {
WindowsApi::show_window(self.hwnd(), SW_SHOW)
}
}

View File

@@ -7,7 +7,6 @@ use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::workspace::Workspace;
use crate::ACTIVE_WINDOW_BORDER_STYLE;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
use crate::BORDER_COLOUR_SINGLE;
@@ -28,12 +27,6 @@ use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REGEX_IDENTIFIERS;
use crate::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::STACKBAR_MODE;
use crate::STACKBAR_TAB_BACKGROUND_COLOUR;
use crate::STACKBAR_TAB_HEIGHT;
use crate::STACKBAR_TAB_WIDTH;
use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
@@ -41,11 +34,9 @@ use color_eyre::Result;
use crossbeam_channel::Receiver;
use hotwatch::EventKind;
use hotwatch::Hotwatch;
use komorebi_core::config_generation::ApplicationConfiguration;
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
use komorebi_core::config_generation::ApplicationOptions;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::resolve_home_path;
use komorebi_core::ApplicationIdentifier;
@@ -83,17 +74,6 @@ pub struct ActiveWindowBorderColours {
pub monocle: Colour,
}
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub enum ActiveWindowBorderStyle {
#[default]
/// Use the system border style
System,
/// Use the Windows 11-style rounded borders
Rounded,
/// Use the Windows 10-style square borders
Square,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct WorkspaceConfig {
/// Name
@@ -272,9 +252,6 @@ pub struct StaticConfig {
/// Active window border colours for different container types
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_colours: Option<ActiveWindowBorderColours>,
/// Active window border style (default: System)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_style: Option<ActiveWindowBorderStyle>,
/// Global default workspace padding (default: 10)
#[serde(skip_serializing_if = "Option::is_none")]
pub default_workspace_padding: Option<i32>,
@@ -292,52 +269,28 @@ pub struct StaticConfig {
pub global_work_area_offset: Option<Rect>,
/// Individual window floating rules
#[serde(skip_serializing_if = "Option::is_none")]
pub float_rules: Option<Vec<MatchingRule>>,
pub float_rules: Option<Vec<IdWithIdentifier>>,
/// Individual window force-manage rules
#[serde(skip_serializing_if = "Option::is_none")]
pub manage_rules: Option<Vec<MatchingRule>>,
pub manage_rules: Option<Vec<IdWithIdentifier>>,
/// Identify border overflow applications
#[serde(skip_serializing_if = "Option::is_none")]
pub border_overflow_applications: Option<Vec<MatchingRule>>,
pub border_overflow_applications: Option<Vec<IdWithIdentifier>>,
/// Identify tray and multi-window applications
#[serde(skip_serializing_if = "Option::is_none")]
pub tray_and_multi_window_applications: Option<Vec<MatchingRule>>,
pub tray_and_multi_window_applications: Option<Vec<IdWithIdentifier>>,
/// Identify applications that have the WS_EX_LAYERED extended window style
#[serde(skip_serializing_if = "Option::is_none")]
pub layered_applications: Option<Vec<MatchingRule>>,
pub layered_applications: Option<Vec<IdWithIdentifier>>,
/// Identify applications that send EVENT_OBJECT_NAMECHANGE on launch (very rare)
#[serde(skip_serializing_if = "Option::is_none")]
pub object_name_change_applications: Option<Vec<MatchingRule>>,
pub object_name_change_applications: Option<Vec<IdWithIdentifier>>,
/// Set monitor index preferences
#[serde(skip_serializing_if = "Option::is_none")]
pub monitor_index_preferences: Option<HashMap<usize, Rect>>,
/// Set display index preferences
#[serde(skip_serializing_if = "Option::is_none")]
pub display_index_preferences: Option<HashMap<usize, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stackbar: Option<StackbarConfig>,
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
pub enum StackbarMode {
Always,
Never,
OnStack,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct TabsConfig {
width: Option<i32>,
focused_text: Option<Colour>,
unfocused_text: Option<Colour>,
background: Option<Colour>,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct StackbarConfig {
height: Option<i32>,
mode: Option<StackbarMode>,
tabs: Option<TabsConfig>,
}
impl From<&WindowManager> for StaticConfig {
@@ -426,7 +379,6 @@ impl From<&WindowManager> for StaticConfig {
border_offset: Option::from(BORDER_OFFSET.load(Ordering::SeqCst)),
active_window_border: Option::from(BORDER_ENABLED.load(Ordering::SeqCst)),
active_window_border_colours: border_colours,
active_window_border_style: Option::from(*ACTIVE_WINDOW_BORDER_STYLE.lock()),
default_workspace_padding: Option::from(
DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst),
),
@@ -444,7 +396,6 @@ impl From<&WindowManager> for StaticConfig {
object_name_change_applications: None,
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
display_index_preferences: Option::from(DISPLAY_INDEX_PREFERENCES.lock().clone()),
stackbar: None,
}
}
}
@@ -493,9 +444,6 @@ impl StaticConfig {
BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst);
}
let active_window_border_style = self.active_window_border_style.unwrap_or_default();
*ACTIVE_WINDOW_BORDER_STYLE.lock() = active_window_border_style;
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
@@ -504,65 +452,104 @@ impl StaticConfig {
let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
let mut layered_identifiers = LAYERED_WHITELIST.lock();
if let Some(rules) = &mut self.float_rules {
populate_rules(rules, &mut float_identifiers, &mut regex_identifiers)?;
}
if let Some(float) = &mut self.float_rules {
for identifier in float {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if let Some(rules) = &mut self.manage_rules {
populate_rules(rules, &mut manage_identifiers, &mut regex_identifiers)?;
}
if !float_identifiers.contains(identifier) {
float_identifiers.push(identifier.clone());
if let Some(rules) = &mut self.object_name_change_applications {
populate_rules(
rules,
&mut object_name_change_identifiers,
&mut regex_identifiers,
)?;
}
if let Some(rules) = &mut self.layered_applications {
populate_rules(rules, &mut layered_identifiers, &mut regex_identifiers)?;
}
if let Some(rules) = &mut self.border_overflow_applications {
populate_rules(
rules,
&mut border_overflow_identifiers,
&mut regex_identifiers,
)?;
}
if let Some(rules) = &mut self.tray_and_multi_window_applications {
populate_rules(
rules,
&mut tray_and_multi_window_identifiers,
&mut regex_identifiers,
)?;
}
if let Some(stackbar) = &self.stackbar {
if let Some(height) = &stackbar.height {
STACKBAR_TAB_HEIGHT.store(*height, Ordering::SeqCst);
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
if let Some(mode) = &stackbar.mode {
let mut stackbar_mode = STACKBAR_MODE.lock();
*stackbar_mode = *mode;
}
if let Some(manage) = &mut self.manage_rules {
for identifier in manage {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !manage_identifiers.contains(identifier) {
manage_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
if let Some(tabs) = &stackbar.tabs {
if let Some(background) = &tabs.background {
STACKBAR_TAB_BACKGROUND_COLOUR.store((*background).into(), Ordering::SeqCst);
}
if let Some(identifiers) = &mut self.object_name_change_applications {
for identifier in identifiers {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if let Some(colour) = &tabs.focused_text {
STACKBAR_FOCUSED_TEXT_COLOUR.store((*colour).into(), Ordering::SeqCst);
if !object_name_change_identifiers.contains(identifier) {
object_name_change_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &mut self.layered_applications {
for identifier in identifiers {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if let Some(colour) = &tabs.unfocused_text {
STACKBAR_UNFOCUSED_TEXT_COLOUR.store((*colour).into(), Ordering::SeqCst);
if !layered_identifiers.contains(identifier) {
layered_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &mut self.border_overflow_applications {
for identifier in identifiers {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if let Some(width) = &tabs.width {
STACKBAR_TAB_WIDTH.store(*width, Ordering::SeqCst);
if !border_overflow_identifiers.contains(identifier) {
border_overflow_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &mut self.tray_and_multi_window_applications {
for identifier in identifiers {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !tray_and_multi_window_identifiers.contains(identifier) {
tray_and_multi_window_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
@@ -573,48 +560,120 @@ impl StaticConfig {
let asc = ApplicationConfigurationGenerator::load(&content)?;
for mut entry in asc {
if let Some(rules) = &mut entry.float_identifiers {
populate_rules(rules, &mut float_identifiers, &mut regex_identifiers)?;
}
if let Some(float) = entry.float_identifiers {
for f in float {
let mut without_comment: IdWithIdentifier = f.into();
if without_comment.matching_strategy.is_none() {
without_comment.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if let Some(ref options) = entry.options {
let options = options.clone();
if !float_identifiers.contains(&without_comment) {
float_identifiers.push(without_comment.clone());
if matches!(
without_comment.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&without_comment.id)?;
regex_identifiers.insert(without_comment.id.clone(), re);
}
}
}
}
if let Some(options) = entry.options {
for o in options {
match o {
ApplicationOptions::ObjectNameChange => {
populate_option(
&mut entry,
&mut object_name_change_identifiers,
&mut regex_identifiers,
)?;
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !object_name_change_identifiers.contains(&entry.identifier) {
object_name_change_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::Layered => {
populate_option(
&mut entry,
&mut layered_identifiers,
&mut regex_identifiers,
)?;
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !layered_identifiers.contains(&entry.identifier) {
layered_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::BorderOverflow => {
populate_option(
&mut entry,
&mut border_overflow_identifiers,
&mut regex_identifiers,
)?;
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !border_overflow_identifiers.contains(&entry.identifier) {
border_overflow_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::TrayAndMultiWindow => {
populate_option(
&mut entry,
&mut tray_and_multi_window_identifiers,
&mut regex_identifiers,
)?;
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !tray_and_multi_window_identifiers.contains(&entry.identifier) {
tray_and_multi_window_identifiers
.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::Force => {
populate_option(
&mut entry,
&mut manage_identifiers,
&mut regex_identifiers,
)?;
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !manage_identifiers.contains(&entry.identifier) {
manage_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
}
}
@@ -842,67 +901,3 @@ impl StaticConfig {
Ok(())
}
}
fn populate_option(
entry: &mut ApplicationConfiguration,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
let rule = MatchingRule::Simple(entry.identifier.clone());
if !identifiers.contains(&rule) {
identifiers.push(rule);
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
Ok(())
}
fn populate_rules(
matching_rules: &mut Vec<MatchingRule>,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
for matching_rule in matching_rules {
if !identifiers.contains(matching_rule) {
match matching_rule {
MatchingRule::Simple(simple) => {
if simple.matching_strategy.is_none() {
simple.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if matches!(simple.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&simple.id)?;
regex_identifiers.insert(simple.id.clone(), re);
}
}
MatchingRule::Composite(composite) => {
for rule in composite {
if rule.matching_strategy.is_none() {
rule.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if matches!(rule.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&rule.id)?;
regex_identifiers.insert(rule.id.clone(), re);
}
}
}
}
identifiers.push(matching_rule.clone());
}
}
Ok(())
}

View File

@@ -4,13 +4,11 @@ use std::convert::TryFrom;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write as _;
use std::time::Duration;
use color_eyre::eyre;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::MatchingStrategy;
use regex::Regex;
use schemars::JsonSchema;
@@ -217,6 +215,57 @@ impl Window {
WindowsApi::unmaximize_window(self.hwnd());
}
pub fn raise(self) {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
let current_thread_id = WindowsApi::current_thread_id();
// This can be allowed to fail if a window doesn't have a message queue or if a journal record
// hook has been installed
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput#remarks
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, true) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not attach to window thread input processing mechanism, but continuing execution of raise(): {}",
error
);
}
};
// Raise Window to foreground
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not set as foreground window, but continuing execution of raise(): {}",
error
);
}
};
// This isn't really needed when the above command works as expected via AHK
match WindowsApi::set_focus(self.hwnd()) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not set focus, but continuing execution of raise(): {}",
error
);
}
};
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, false) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not detach from window thread input processing mechanism, but continuing execution of raise(): {}",
error
);
}
};
}
pub fn focus(self, mouse_follows_focus: bool) -> Result<()> {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
@@ -475,10 +524,6 @@ fn window_is_eligible(
titlebars_removed.contains(exe_name)
};
if exe_name.contains("firefox") {
std::thread::sleep(Duration::from_millis(10));
}
if (allow_wsl2_gui || allow_titlebar_removed || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
&& !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME)
// Get a lot of dupe events coming through that make the redrawing go crazy
@@ -507,290 +552,157 @@ pub fn should_act(
exe_name: &str,
class: &str,
path: &str,
identifiers: &[MatchingRule],
identifiers: &[IdWithIdentifier],
regex_identifiers: &HashMap<String, Regex>,
) -> bool {
let mut should_act = false;
for identifier in identifiers {
match identifier {
MatchingRule::Simple(identifier) => {
if should_act_individual(
title,
exe_name,
class,
path,
identifier,
regex_identifiers,
) {
should_act = true
};
match identifier.matching_strategy {
None => {
panic!("there is no matching strategy identified for this rule");
}
MatchingRule::Composite(identifiers) => {
let mut composite_results = vec![];
for identifier in identifiers {
composite_results.push(should_act_individual(
title,
exe_name,
class,
path,
identifier,
regex_identifiers,
));
Some(MatchingStrategy::Legacy) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) {
should_act = true;
}
}
if composite_results.iter().all(|&x| x) {
should_act = true;
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Equals) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::StartsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::EndsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Contains) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
}
}
ApplicationIdentifier::Path => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(path) {
should_act = true;
}
}
}
},
}
}
should_act
}
pub fn should_act_individual(
title: &str,
exe_name: &str,
class: &str,
path: &str,
identifier: &IdWithIdentifier,
regex_identifiers: &HashMap<String, Regex>,
) -> bool {
let mut should_act = false;
match identifier.matching_strategy {
None => {
panic!("there is no matching strategy identified for this rule");
}
Some(MatchingStrategy::Legacy) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Equals) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::DoesNotEqual) => match identifier.kind {
ApplicationIdentifier::Title => {
if !title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if !class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if !exe_name.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if !path.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::StartsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::DoesNotStartWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if !title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if !class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if !exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if !path.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::EndsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::DoesNotEndWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if !title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if !class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if !exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if !path.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Contains) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if path.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::DoesNotContain) => match identifier.kind {
ApplicationIdentifier::Title => {
if !title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if !class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if !exe_name.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Path => {
if !path.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
}
}
ApplicationIdentifier::Path => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(path) {
should_act = true;
}
}
}
},
}
should_act
}

View File

@@ -21,7 +21,7 @@ use serde::Deserialize;
use serde::Serialize;
use uds_windows::UnixListener;
use komorebi_core::config_generation::MatchingRule;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement;
use komorebi_core::Axis;
@@ -97,12 +97,12 @@ pub struct State {
pub mouse_follows_focus: bool,
pub has_pending_raise_op: bool,
pub remove_titlebars: bool,
pub float_identifiers: Vec<MatchingRule>,
pub manage_identifiers: Vec<MatchingRule>,
pub layered_whitelist: Vec<MatchingRule>,
pub tray_and_multi_window_identifiers: Vec<MatchingRule>,
pub border_overflow_identifiers: Vec<MatchingRule>,
pub name_change_on_launch_identifiers: Vec<MatchingRule>,
pub float_identifiers: Vec<IdWithIdentifier>,
pub manage_identifiers: Vec<IdWithIdentifier>,
pub layered_whitelist: Vec<IdWithIdentifier>,
pub tray_and_multi_window_identifiers: Vec<IdWithIdentifier>,
pub border_overflow_identifiers: Vec<IdWithIdentifier>,
pub name_change_on_launch_identifiers: Vec<IdWithIdentifier>,
pub monitor_index_preferences: HashMap<usize, Rect>,
pub display_index_preferences: HashMap<usize, String>,
}
@@ -671,44 +671,63 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn raise_window_at_cursor_pos(&mut self) -> Result<()> {
let mut hwnd = None;
let mut hwnd = WindowsApi::window_at_cursor_pos()?;
for monitor in self.monitors() {
for workspace in monitor.workspaces() {
if let Some(container_idx) = workspace.container_idx_from_current_point() {
if let Some(container) = workspace.containers().get(container_idx) {
if let Some(window) = container.focused_window() {
hwnd = Some(window.hwnd);
if self.has_pending_raise_op
|| self.focused_window()?.hwnd == hwnd
// Sometimes we need this check, because the focus may have been given by a click
// to a non-window such as the taskbar or system tray, and komorebi doesn't know that
// the focused window of the workspace is not actually focused by the OS at that point
|| WindowsApi::foreground_window()? == hwnd
{
Ok(())
} else {
let mut known_hwnd = false;
for monitor in self.monitors() {
for workspace in monitor.workspaces() {
if workspace.contains_window(hwnd) {
known_hwnd = true;
}
}
}
// TODO: Not sure if this needs to be made configurable just yet...
let overlay_classes = [
// Chromium/Electron
"Chrome_RenderWidgetHostHWND".to_string(),
// Explorer
"DirectUIHWND".to_string(),
"SysTreeView32".to_string(),
"ToolbarWindow32".to_string(),
"NetUIHWND".to_string(),
];
if !known_hwnd {
let class = Window { hwnd }.class()?;
// Some applications (Electron/Chromium-based, explorer) have (invisible?) overlays
// windows that we need to look beyond to find the actual window to raise
if overlay_classes.contains(&class) {
for monitor in self.monitors() {
for workspace in monitor.workspaces() {
if let Some(exe_hwnd) = workspace.hwnd_from_exe(&Window { hwnd }.exe()?)
{
hwnd = exe_hwnd;
known_hwnd = true;
}
}
}
}
}
}
if let Some(hwnd) = hwnd {
if self.has_pending_raise_op
|| self.focused_window()?.hwnd == hwnd
// Sometimes we need this check, because the focus may have been given by a click
// to a non-window such as the taskbar or system tray, and komorebi doesn't know that
// the focused window of the workspace is not actually focused by the OS at that point
|| WindowsApi::foreground_window()? == hwnd
{
return Ok(());
if known_hwnd {
let event = WindowManagerEvent::Raise(Window { hwnd });
self.has_pending_raise_op = true;
Ok(winevent_listener::event_tx().send(event)?)
} else {
tracing::debug!("not raising unknown window: {}", Window { hwnd });
Ok(())
}
let event = WindowManagerEvent::Raise(Window { hwnd });
self.has_pending_raise_op = true;
winevent_listener::event_tx().send(event)?;
} else {
tracing::debug!(
"not raising unknown window: {}",
Window {
hwnd: WindowsApi::window_at_cursor_pos()?
}
);
}
Ok(())
}
#[tracing::instrument(skip(self))]
@@ -841,16 +860,16 @@ impl WindowManager {
if !follow_focus && self.focused_container_mut().is_ok() {
// and we have a stack with >1 windows
if self.focused_container_mut()?.windows().len() > 1
// and we don't have a maxed window
&& self.focused_workspace()?.maximized_window().is_none()
// and we don't have a monocle container
&& self.focused_workspace()?.monocle_container().is_none()
// and we don't have a maxed window
&& self.focused_workspace()?.maximized_window().is_none()
// and we don't have a monocle container
&& self.focused_workspace()?.monocle_container().is_none()
{
if let Ok(window) = self.focused_window_mut() {
window.focus(self.mouse_follows_focus)?;
}
}
}
};
// This is to correctly restore and focus when switching to a workspace which
// contains a managed maximized window
@@ -1197,21 +1216,7 @@ impl WindowManager {
}
}
// When switching workspaces and landing focus on a window that is not stack, but a stack
// exists, and there is a stackbar visible, when changing focus to that container stack,
// the focused text colour will not be applied until the stack has been cycled at least once
//
// With this piece of code, we check if we have changed focus to a container stack with
// a stackbar, and if we have, we run a quick update to make sure the focused text colour
// has been applied
let focused_window = self.focused_window_mut()?;
let focused_window_hwnd = focused_window.hwnd;
focused_window.focus(self.mouse_follows_focus)?;
let focused_container = self.focused_container()?;
if let Some(stackbar) = focused_container.stackbar() {
stackbar.update(focused_container.windows(), focused_window_hwnd)?;
}
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;
Ok(())
}

View File

@@ -28,7 +28,6 @@ pub enum WindowManagerEvent {
Unmanage(Window),
Raise(Window),
DisplayChange(Window),
ForceUpdate(Window),
}
impl Display for WindowManagerEvent {
@@ -79,9 +78,6 @@ impl Display for WindowManagerEvent {
Self::DisplayChange(window) => {
write!(f, "DisplayChange (Window: {window})")
}
Self::ForceUpdate(window) => {
write!(f, "ForceUpdate (Window: {window})")
}
}
}
}
@@ -102,8 +98,7 @@ impl WindowManagerEvent {
| Self::Raise(window)
| Self::Manage(window)
| Self::DisplayChange(window)
| Self::Unmanage(window)
| Self::ForceUpdate(window) => window,
| Self::Unmanage(window) => window,
}
}

View File

@@ -36,8 +36,6 @@ use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::RoundRect;
use windows::Win32::Graphics::Gdi::DISPLAY_DEVICEW;
use windows::Win32::Graphics::Gdi::HBRUSH;
use windows::Win32::Graphics::Gdi::HDC;
@@ -68,7 +66,6 @@ use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_LBUTTON;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
@@ -421,7 +418,7 @@ impl WindowsApi {
.process()
}
pub fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {
// BOOL is returned but does not signify whether or not the operation was succesful
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
unsafe { ShowWindow(hwnd, command) };
@@ -530,24 +527,6 @@ impl WindowsApi {
})
}
pub fn round_rect(hdc: HDC, rect: &Rect, border_radius: i32) {
unsafe {
RoundRect(
hdc,
rect.left,
rect.top,
rect.right,
rect.bottom,
border_radius,
border_radius,
);
}
}
pub fn rectangle(hdc: HDC, rect: &Rect) {
unsafe {
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
}
}
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
unsafe { SetCursorPos(x, y) }.process()
}
@@ -985,13 +964,6 @@ impl WindowsApi {
actual != 0
}
pub fn lbutton_is_pressed() -> bool {
let state = unsafe { GetKeyState(i32::from(VK_LBUTTON.0)) };
#[allow(clippy::cast_sign_loss)]
let actual = (state as u16) & 0x8000;
actual != 0
}
pub fn left_click() -> u32 {
let inputs = [
INPUT {

View File

@@ -41,8 +41,6 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::winevent_listener;
use crate::ActiveWindowBorderStyle;
use crate::ACTIVE_WINDOW_BORDER_STYLE;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
use crate::BORDER_WIDTH;
@@ -234,20 +232,10 @@ pub extern "system" fn border_window(
// the window was made with DWMWCP_ROUNDSMALL then this is the
// wrong size. In the future we should read the DWM properties
// of windows and attempt to match appropriately.
match *ACTIVE_WINDOW_BORDER_STYLE.lock() {
ActiveWindowBorderStyle::System => {
if *WINDOWS_11 {
RoundRect(hdc, 0, 0, border_rect.right, border_rect.bottom, 20, 20);
} else {
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
}
}
ActiveWindowBorderStyle::Rounded => {
RoundRect(hdc, 0, 0, border_rect.right, border_rect.bottom, 20, 20);
}
ActiveWindowBorderStyle::Square => {
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
}
if *WINDOWS_11 {
RoundRect(hdc, 0, 0, border_rect.right, border_rect.bottom, 20, 20);
} else {
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
}
EndPaint(window, &ps);
ValidateRect(window, None);

View File

@@ -33,7 +33,6 @@ use crate::DEFAULT_WORKSPACE_PADDING;
use crate::INITIAL_CONFIGURATION_LOADED;
use crate::NO_TITLEBAR;
use crate::REMOVE_TITLEBARS;
use crate::STACKBAR_TAB_HEIGHT;
#[allow(clippy::struct_field_names)]
#[derive(
@@ -144,66 +143,55 @@ impl Workspace {
Ok(())
}
pub fn hide(&mut self, omit: Option<isize>) {
for window in self.floating_windows_mut().iter_mut().rev() {
let mut should_hide = omit.is_none();
if !should_hide {
if let Some(omit) = omit {
if omit != window.hwnd {
should_hide = true
}
}
}
if should_hide {
pub fn hide(&mut self) {
for container in self.containers_mut() {
for window in container.windows_mut() {
window.hide();
}
}
for container in self.containers_mut() {
container.hide(omit)
}
if let Some(window) = self.maximized_window() {
window.hide();
}
if let Some(container) = self.monocle_container_mut() {
container.hide(omit)
for window in container.windows_mut() {
window.hide();
}
}
for window in self.floating_windows() {
window.hide();
}
}
pub fn restore(&mut self, mouse_follows_focus: bool) -> Result<()> {
let idx = self.focused_container_idx();
let mut to_focus = None;
for (i, container) in self.containers_mut().iter_mut().enumerate() {
if let Some(window) = container.focused_window_mut() {
window.restore();
if idx == i {
to_focus = Option::from(*window);
}
}
container.restore();
}
for container in self.containers_mut() {
container.restore();
if let Some(window) = self.maximized_window() {
window.maximize();
}
if let Some(container) = self.monocle_container_mut() {
container.restore();
for window in container.windows_mut() {
window.restore();
}
}
for window in self.floating_windows() {
window.restore();
}
if let Some(container) = self.focused_container_mut() {
container.focus_window(container.focused_window_idx());
}
// Do this here to make sure that an error doesn't stop the restoration of other windows
// Maximised windows should always be drawn at the top of the Z order
if let Some(window) = to_focus {
@@ -288,23 +276,9 @@ impl Workspace {
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
let no_titlebar = NO_TITLEBAR.lock().clone();
let focused_hwnd = self
.focused_container()
.ok_or_else(|| anyhow!("couldn't find a focused container"))?
.focused_window()
.ok_or_else(|| anyhow!("couldn't find a focused window"))?
.hwnd;
let container_padding = self.container_padding().unwrap_or(0);
let containers = self.containers_mut();
for (i, container) in containers.iter_mut().enumerate() {
let container_windows = container.windows().clone();
let container_topbar = container.stackbar().clone();
if let (Some(window), Some(layout)) =
(container.focused_window_mut(), layouts.get(i))
{
let windows = self.visible_windows_mut();
for (i, window) in windows.into_iter().enumerate() {
if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
window.remove_title_bar()?;
} else if no_titlebar.contains(&window.exe()?) {
@@ -326,20 +300,6 @@ impl Workspace {
rect.add_padding(width);
}
if let Some(stackbar) = container_topbar {
stackbar.set_position(
&stackbar.get_position_from_container_layout(layout),
false,
)?;
stackbar.update(&container_windows, focused_hwnd)?;
let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst);
let total_height = tab_height + container_padding;
rect.top += total_height;
rect.bottom -= total_height;
}
window.set_position(&rect, false)?;
}
}
@@ -415,18 +375,7 @@ impl Workspace {
.idx_for_window(hwnd)
.ok_or_else(|| anyhow!("there is no window"))?;
let mut should_load = false;
if container.focused_window_idx() != window_idx {
should_load = true
}
container.focus_window(window_idx);
if should_load {
container.load_focused_window();
}
self.focus_container(container_idx);
Ok(())

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic-no-console"
version = "0.1.23-dev.0"
version = "0.1.22"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.23-dev.0"
version = "0.1.22"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]
@@ -19,7 +19,7 @@ color-eyre = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
fs-tail = "0.1"
heck = "0.5"
heck = "0.4"
lazy_static = "1"
miette = { version = "7", features = ["fancy"] }
paste = "1"
@@ -32,7 +32,4 @@ sysinfo = "0.30"
thiserror = "1"
uds_windows = "1"
which = "6"
windows = { workspace = true }
[build-dependencies]
reqwest = { version = "0.11", features = ["blocking"] }
windows = { workspace = true }

View File

@@ -1,8 +0,0 @@
fn main() {
if std::fs::metadata("applications.yaml").is_err() {
let applications_yaml = reqwest::blocking::get(
"https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml"
).unwrap().text().unwrap();
std::fs::write("applications.yaml", applications_yaml).unwrap();
}
}

View File

@@ -1256,6 +1256,8 @@ fn main() -> Result<()> {
}
}
SubCommand::Quickstart => {
let version = env!("CARGO_PKG_VERSION");
let home_dir = dirs::home_dir().expect("could not find home dir");
let config_dir = home_dir.join(".config");
let local_appdata_dir = data_local_dir().expect("could not find localdata dir");
@@ -1263,13 +1265,21 @@ fn main() -> Result<()> {
std::fs::create_dir_all(&config_dir)?;
std::fs::create_dir_all(data_dir)?;
let komorebi_json = include_str!("../../docs/komorebi.example.json");
let komorebi_json = reqwest::blocking::get(
format!("https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/komorebi.example.json")
)?.text()?;
std::fs::write(HOME_DIR.join("komorebi.json"), komorebi_json)?;
let applications_yaml = include_str!("../applications.yaml");
let applications_yaml = reqwest::blocking::get(
"https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.yaml"
)?
.text()?;
std::fs::write(HOME_DIR.join("applications.yaml"), applications_yaml)?;
let whkdrc = include_str!("../../docs/whkdrc.sample");
let whkdrc = reqwest::blocking::get(format!(
"https://raw.githubusercontent.com/LGUG2Z/komorebi/v{version}/whkdrc.sample"
))?
.text()?;
std::fs::write(config_dir.join("whkdrc"), whkdrc)?;
println!("Example ~/komorebi.json, ~/.config/whkdrc and latest ~/applications.yaml files downloaded");
@@ -1871,13 +1881,6 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
}
}
}
println!("\nThank you for using komorebi!\n");
println!("* Become a sponsor https://github.com/sponsors/LGUG2Z - Even $1/month makes a big difference");
println!(
"* Subscribe to https://youtube.com/@LGUG2Z - Live dev videos and feature previews"
);
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
}
SubCommand::Stop(arg) => {
if arg.whkd {

63
whkdrc.sample Normal file
View File

@@ -0,0 +1,63 @@
.shell powershell
# Reload whkd configuration
# alt + o : taskkill /f /im whkd.exe && start /b whkd # if shell is cmd
alt + o : taskkill /f /im whkd.exe; Start-Process whkd -WindowStyle hidden # if shell is pwsh / powershell
alt + shift + o : komorebic reload-configuration
# App shortcuts - these require shell to be pwsh / powershell
# The apps will be focused if open, or launched if not open
# alt + f : if ($wshell.AppActivate('Firefox') -eq $False) { start firefox }
# alt + b : if ($wshell.AppActivate('Chrome') -eq $False) { start chrome }
# Focus windows
alt + h : komorebic focus left
alt + j : komorebic focus down
alt + k : komorebic focus up
alt + l : komorebic focus right
alt + shift + oem_4 : komorebic cycle-focus previous # oem_4 is [
alt + shift + oem_6 : komorebic cycle-focus next # oem_6 is ]
# Move windows
alt + shift + h : komorebic move left
alt + shift + j : komorebic move down
alt + shift + k : komorebic move up
alt + shift + l : komorebic move right
alt + shift + return : komorebic promote
# Stack windows
alt + left : komorebic stack left
alt + down : komorebic stack down
alt + up : komorebic stack up
alt + right : komorebic stack right
alt + oem_1 : komorebic unstack # oem_1 is ;
alt + oem_4 : komorebic cycle-stack previous # oem_4 is [
alt + oem_6 : komorebic cycle-stack next # oem_6 is ]
# Resize
alt + oem_plus : komorebic resize-axis horizontal increase
alt + oem_minus : komorebic resize-axis horizontal decrease
alt + shift + oem_plus : komorebic resize-axis vertical increase
alt + shift + oem_minus : komorebic resize-axis vertical decrease
# Manipulate windows
alt + t : komorebic toggle-float
alt + shift + f : komorebic toggle-monocle
# Window manager options
alt + shift + r : komorebic retile
alt + p : komorebic toggle-pause
# Layouts
alt + x : komorebic flip-layout horizontal
alt + y : komorebic flip-layout vertical
# Workspaces
alt + 1 : komorebic focus-workspace 0
alt + 2 : komorebic focus-workspace 1
alt + 3 : komorebic focus-workspace 2
# Move windows across workspaces
alt + shift + 1 : komorebic move-to-workspace 0
alt + shift + 2 : komorebic move-to-workspace 1
alt + shift + 3 : komorebic move-to-workspace 2