Compare commits

..

2 Commits

Author SHA1 Message Date
LGUG2Z
ffa9d4aac5 test 2024-02-18 15:05:35 -08:00
LGUG2Z
6a03849e0a refactor(wm): split komorebi into bin and lib
This commit splits the komorebi crate into a mixed binary and library
crate.

All types and logic related to window management have been moved under
lib.rs, and are imported from there for use in main.rs, which is now
only responsible for starting and running the window manager process.

In preparation for exposing a new komorebi-client crate in the future,
serde::Deserialize has been derived on Notification and any struct that
may appear in a notification receievd by a process that has subscribed
to event updates.

re #659
2024-02-18 14:41:12 -08:00
27 changed files with 604 additions and 922 deletions

163
Cargo.lock generated
View File

@@ -28,9 +28,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.12"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540"
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -74,12 +74,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -130,9 +124,9 @@ checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "bumpalo"
version = "3.15.3"
version = "3.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f"
[[package]]
name = "bytes"
@@ -142,9 +136,12 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
version = "1.0.87"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3286b845d0fccbdd15af433f61c5970e711987036cb468f437ff6badd70f4e24"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
@@ -190,7 +187,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -592,20 +589,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.8"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "379dada1584ad501b383485dd706b8afb7a70fcbc7f4da7d780638a5a6124a60"
[[package]]
name = "hex_color"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d37f101bf4c633f7ca2e4b5e136050314503dd198e78e325ea602c327c484ef0"
dependencies = [
"arrayvec",
"rand",
"serde",
]
checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd"
[[package]]
name = "home"
@@ -812,7 +798,6 @@ dependencies = [
"ctrlc",
"dirs",
"getset",
"hex_color",
"hotwatch",
"komorebi-core",
"lazy_static",
@@ -992,7 +977,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -1192,9 +1177,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.64"
version = "0.10.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8"
dependencies = [
"bitflags 2.4.2",
"cfg-if 1.0.0",
@@ -1213,7 +1198,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -1224,9 +1209,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.101"
version = "0.9.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae"
dependencies = [
"cc",
"libc",
@@ -1579,9 +1564,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.17"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
[[package]]
name = "same-file"
@@ -1656,22 +1641,22 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.197"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
version = "1.0.196"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -1687,9 +1672,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.114"
version = "1.0.113"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
dependencies = [
"itoa",
"ryu",
@@ -1710,9 +1695,9 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.32"
version = "0.9.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f"
checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e"
dependencies = [
"indexmap",
"itoa",
@@ -1753,12 +1738,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "socket2"
version = "0.5.6"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -1786,7 +1771,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -1830,9 +1815,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.50"
version = "2.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb"
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
dependencies = [
"proc-macro2",
"quote",
@@ -1941,7 +1926,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -1956,9 +1941,9 @@ dependencies = [
[[package]]
name = "thread_local"
version = "1.1.8"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
@@ -2087,7 +2072,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -2176,9 +2161,9 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
@@ -2276,7 +2261,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
"wasm-bindgen-shared",
]
@@ -2310,7 +2295,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -2333,15 +2318,15 @@ dependencies = [
[[package]]
name = "which"
version = "6.0.0"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fa5e0c10bf77f44aac573e498d1a82d5fbd5e91f6fc0a99e7be4b38e85e101c"
checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.48.0",
]
[[package]]
@@ -2402,7 +2387,7 @@ dependencies = [
"windows-core",
"windows-implement",
"windows-interface",
"windows-targets 0.52.3",
"windows-targets 0.52.0",
]
[[package]]
@@ -2411,7 +2396,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.3",
"windows-targets 0.52.0",
]
[[package]]
@@ -2422,7 +2407,7 @@ checksum = "12168c33176773b86799be25e2a2ba07c7aab9968b37541f1094dbd7a60c8946"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -2433,7 +2418,7 @@ checksum = "9d8dc32e0095a7eeccebd0e3f09e9509365ecb3fc6ac4d6f5f14a3f6392942d1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.50",
"syn 2.0.49",
]
[[package]]
@@ -2466,7 +2451,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.3",
"windows-targets 0.52.0",
]
[[package]]
@@ -2486,17 +2471,17 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.3",
"windows_aarch64_msvc 0.52.3",
"windows_i686_gnu 0.52.3",
"windows_i686_msvc 0.52.3",
"windows_x86_64_gnu 0.52.3",
"windows_x86_64_gnullvm 0.52.3",
"windows_x86_64_msvc 0.52.3",
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
@@ -2513,9 +2498,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
@@ -2531,9 +2516,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
@@ -2549,9 +2534,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
@@ -2567,9 +2552,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
@@ -2585,9 +2570,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
@@ -2603,9 +2588,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
@@ -2621,9 +2606,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.3"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winput"

View File

@@ -34,5 +34,7 @@ windows managed by `komorebi`.
This feature is not considered stable and you may encounter visual artifacts
from time to time.
<!-- TODO: Record a new video -->
[![Watch the tutorial
video](https://img.youtube.com/vi/7_9D22t7KK4/hqdefault.jpg)](https://www.youtube.com/watch?v=7_9D22t7KK4)
video](https://img.youtube.com/vi/ywiAvoMV_gE/hqdefault.jpg)](https://www.youtube.com/watch?v=ywiAvoMV_gE)

View File

@@ -25,6 +25,3 @@ If you already have configuration files that you wish to keep, move them to the
The next time you run `komorebic start`, any files created by or loaded by
_komorebi_ will be placed or expected to exist in this folder.
[![Watch the tutorial
video](https://img.youtube.com/vi/C_KWUqQ6kko/hqdefault.jpg)](https://www.youtube.com/watch?v=C_KWUqQ6kko)

View File

@@ -12,6 +12,6 @@ file.
}
```
A restart of `komorebi` is required after changing these settings.
<!-- TODO: Record a new video -->
[![Watch the tutorial video](https://img.youtube.com/vi/6QYLao953XE/hqdefault.jpg)](https://www.youtube.com/watch?v=6QYLao953XE)
[![Watch the tutorial video](https://img.youtube.com/vi/eGr07mymgWE/hqdefault.jpg)](https://www.youtube.com/watch?v=eGr07mymgWE)

View File

@@ -131,65 +131,11 @@ impl Arrangement for DefaultLayout {
layouts
}
Self::UltrawideVerticalStack => ultrawide(area, len, layout_flip, resize_dimensions),
#[allow(
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap
)]
Self::Grid => {
// Shamelessly lifted from LeftWM
// https://github.com/leftwm/leftwm/blob/18675067b8450e520ef75db2ebbb0d973aa1199e/leftwm-core/src/layouts/grid_horizontal.rs
let mut layouts: Vec<Rect> = vec![];
layouts.resize(len, Rect::default());
let len = len as i32;
let num_cols = (len as f32).sqrt().ceil() as i32;
let mut iter = layouts.iter_mut().enumerate().peekable();
for col in 0..num_cols {
let iter_peek = iter.peek().map(|x| x.0).unwrap_or_default() as i32;
let remaining_windows = len - iter_peek;
let remaining_columns = num_cols - col;
let num_rows_in_this_col = remaining_windows / remaining_columns;
let win_height = area.bottom / num_rows_in_this_col;
let win_width = area.right / num_cols;
for row in 0..num_rows_in_this_col {
if let Some((_idx, win)) = iter.next() {
let mut left = area.left + win_width * col;
let mut top = area.top + win_height * row;
match layout_flip {
Some(Axis::Horizontal) => {
left = area.right - win_width * (col + 1) + area.left;
}
Some(Axis::Vertical) => {
top = area.bottom - win_height * (row + 1) + area.top;
}
Some(Axis::HorizontalAndVertical) => {
left = area.right - win_width * (col + 1) + area.left;
top = area.bottom - win_height * (row + 1) + area.top;
}
None => {} // No flip
}
win.bottom = win_height;
win.right = win_width;
win.left = left;
win.top = top;
}
}
}
layouts
}
};
dimensions
.iter_mut()
.for_each(|l| l.add_padding(container_padding.unwrap_or_default()));
.for_each(|l| l.add_padding(container_padding));
dimensions
}
@@ -312,7 +258,7 @@ impl Arrangement for CustomLayout {
dimensions
.iter_mut()
.for_each(|l| l.add_padding(container_padding.unwrap_or_default()));
.for_each(|l| l.add_padding(container_padding));
dimensions
}

View File

@@ -20,7 +20,6 @@ pub enum DefaultLayout {
VerticalStack,
HorizontalStack,
UltrawideVerticalStack,
Grid,
// NOTE: If any new layout is added, please make sure to register the same in `DefaultLayout::cycle`
}
@@ -136,8 +135,7 @@ impl DefaultLayout {
Self::Rows => Self::VerticalStack,
Self::VerticalStack => Self::HorizontalStack,
Self::HorizontalStack => Self::UltrawideVerticalStack,
Self::UltrawideVerticalStack => Self::Grid,
Self::Grid => Self::BSP,
Self::UltrawideVerticalStack => Self::BSP,
}
}
@@ -149,8 +147,7 @@ impl DefaultLayout {
Self::HorizontalStack => Self::VerticalStack,
Self::VerticalStack => Self::Rows,
Self::Rows => Self::Columns,
Self::Columns => Self::Grid,
Self::Grid => Self::BSP,
Self::Columns => Self::BSP,
}
}
}

View File

@@ -19,30 +19,10 @@ pub trait Direction {
idx: usize,
count: usize,
) -> bool;
fn up_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize;
fn down_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize;
fn left_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize;
fn right_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize;
fn up_index(&self, idx: usize) -> usize;
fn down_index(&self, idx: usize) -> usize;
fn left_index(&self, idx: usize) -> usize;
fn right_index(&self, idx: usize) -> usize;
}
impl Direction for DefaultLayout {
@@ -55,28 +35,28 @@ impl Direction for DefaultLayout {
match op_direction {
OperationDirection::Left => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.left_index(Some(op_direction), idx, Some(count)))
Option::from(self.left_index(idx))
} else {
None
}
}
OperationDirection::Right => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.right_index(Some(op_direction), idx, Some(count)))
Option::from(self.right_index(idx))
} else {
None
}
}
OperationDirection::Up => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.up_index(Some(op_direction), idx, Some(count)))
Option::from(self.up_index(idx))
} else {
None
}
}
OperationDirection::Down => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.down_index(Some(op_direction), idx, Some(count)))
Option::from(self.down_index(idx))
} else {
None
}
@@ -97,7 +77,6 @@ impl Direction for DefaultLayout {
Self::Rows | Self::HorizontalStack => idx != 0,
Self::VerticalStack => idx != 0 && idx != 1,
Self::UltrawideVerticalStack => idx > 2,
Self::Grid => !is_grid_edge(op_direction, idx, count),
},
OperationDirection::Down => match self {
Self::BSP => count > 2 && idx != count - 1 && idx % 2 != 0,
@@ -106,7 +85,6 @@ impl Direction for DefaultLayout {
Self::VerticalStack => idx != 0 && idx != count - 1,
Self::HorizontalStack => idx == 0,
Self::UltrawideVerticalStack => idx > 1 && idx != count - 1,
Self::Grid => !is_grid_edge(op_direction, idx, count),
},
OperationDirection::Left => match self {
Self::BSP => count > 1 && idx != 0,
@@ -114,7 +92,6 @@ impl Direction for DefaultLayout {
Self::Rows => false,
Self::HorizontalStack => idx != 0 && idx != 1,
Self::UltrawideVerticalStack => count > 1 && idx != 1,
Self::Grid => !is_grid_edge(op_direction, idx, count),
},
OperationDirection::Right => match self {
Self::BSP => count > 1 && idx % 2 == 0 && idx != count - 1,
@@ -127,17 +104,11 @@ impl Direction for DefaultLayout {
2 => idx != 0,
_ => idx < 2,
},
Self::Grid => !is_grid_edge(op_direction, idx, count),
},
}
}
fn up_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize {
fn up_index(&self, idx: usize) -> usize {
match self {
Self::BSP => {
if idx % 2 == 0 {
@@ -149,30 +120,18 @@ impl Direction for DefaultLayout {
Self::Columns => unreachable!(),
Self::Rows | Self::VerticalStack | Self::UltrawideVerticalStack => idx - 1,
Self::HorizontalStack => 0,
Self::Grid => grid_neighbor(op_direction, idx, count),
}
}
fn down_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize {
fn down_index(&self, idx: usize) -> usize {
match self {
Self::BSP | Self::Rows | Self::VerticalStack | Self::UltrawideVerticalStack => idx + 1,
Self::Columns => unreachable!(),
Self::HorizontalStack => 1,
Self::Grid => grid_neighbor(op_direction, idx, count),
}
}
fn left_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize {
fn left_index(&self, idx: usize) -> usize {
match self {
Self::BSP => {
if idx % 2 == 0 {
@@ -189,16 +148,10 @@ impl Direction for DefaultLayout {
1 => unreachable!(),
_ => 0,
},
Self::Grid => grid_neighbor(op_direction, idx, count),
}
}
fn right_index(
&self,
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize {
fn right_index(&self, idx: usize) -> usize {
match self {
Self::BSP | Self::Columns | Self::HorizontalStack => idx + 1,
Self::Rows => unreachable!(),
@@ -208,126 +161,10 @@ impl Direction for DefaultLayout {
0 => 2,
_ => unreachable!(),
},
Self::Grid => grid_neighbor(op_direction, idx, count),
}
}
}
struct GridItem {
state: GridItemState,
row: usize,
num_rows: usize,
touching_edges: GridTouchingEdges,
}
enum GridItemState {
Valid,
Invalid,
}
#[allow(clippy::struct_excessive_bools)]
struct GridTouchingEdges {
left: bool,
right: bool,
up: bool,
down: bool,
}
#[allow(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss
)]
fn get_grid_item(idx: usize, count: usize) -> GridItem {
let num_cols = (count as f32).sqrt().ceil() as usize;
let mut iter = 0;
for col in 0..num_cols {
let remaining_windows = count - iter;
let remaining_columns = num_cols - col;
let num_rows_in_this_col = remaining_windows / remaining_columns;
for row in 0..num_rows_in_this_col {
if iter == idx {
return GridItem {
state: GridItemState::Valid,
row: row + 1,
num_rows: num_rows_in_this_col,
touching_edges: GridTouchingEdges {
left: col == 0,
right: col == num_cols - 1,
up: row == 0,
down: row == num_rows_in_this_col - 1,
},
};
}
iter += 1;
}
}
GridItem {
state: GridItemState::Invalid,
row: 0,
num_rows: 0,
touching_edges: GridTouchingEdges {
left: true,
right: true,
up: true,
down: true,
},
}
}
fn is_grid_edge(op_direction: OperationDirection, idx: usize, count: usize) -> bool {
let item = get_grid_item(idx, count);
match item.state {
GridItemState::Invalid => false,
GridItemState::Valid => match op_direction {
OperationDirection::Left => item.touching_edges.left,
OperationDirection::Right => item.touching_edges.right,
OperationDirection::Up => item.touching_edges.up,
OperationDirection::Down => item.touching_edges.down,
},
}
}
fn grid_neighbor(
op_direction: Option<OperationDirection>,
idx: usize,
count: Option<usize>,
) -> usize {
let Some(op_direction) = op_direction else {
return 0;
};
let Some(count) = count else {
return 0;
};
let item = get_grid_item(idx, count);
match op_direction {
OperationDirection::Left => {
let item_from_prev_col = get_grid_item(idx - item.row, count);
if item.touching_edges.up && item.num_rows != item_from_prev_col.num_rows {
return idx - (item.num_rows - 1);
}
if item.num_rows != item_from_prev_col.num_rows && !item.touching_edges.down {
return idx - (item.num_rows - 1);
}
idx - item.num_rows
}
OperationDirection::Right => idx + item.num_rows,
OperationDirection::Up => idx - 1,
OperationDirection::Down => idx + 1,
}
}
impl Direction for CustomLayout {
fn index_in_direction(
&self,
@@ -342,28 +179,28 @@ impl Direction for CustomLayout {
match op_direction {
OperationDirection::Left => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.left_index(None, idx, None))
Option::from(self.left_index(idx))
} else {
None
}
}
OperationDirection::Right => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.right_index(None, idx, None))
Option::from(self.right_index(idx))
} else {
None
}
}
OperationDirection::Up => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.up_index(None, idx, None))
Option::from(self.up_index(idx))
} else {
None
}
}
OperationDirection::Down => {
if self.is_valid_direction(op_direction, idx, count) {
Option::from(self.down_index(None, idx, None))
Option::from(self.down_index(idx))
} else {
None
}
@@ -417,30 +254,15 @@ impl Direction for CustomLayout {
}
}
fn up_index(
&self,
_op_direction: Option<OperationDirection>,
idx: usize,
_count: Option<usize>,
) -> usize {
fn up_index(&self, idx: usize) -> usize {
idx - 1
}
fn down_index(
&self,
_op_direction: Option<OperationDirection>,
idx: usize,
_count: Option<usize>,
) -> usize {
fn down_index(&self, idx: usize) -> usize {
idx + 1
}
fn left_index(
&self,
_op_direction: Option<OperationDirection>,
idx: usize,
_count: Option<usize>,
) -> usize {
fn left_index(&self, idx: usize) -> usize {
let column_idx = self.column_for_container_idx(idx);
if column_idx - 1 == 0 {
0
@@ -449,12 +271,7 @@ impl Direction for CustomLayout {
}
}
fn right_index(
&self,
_op_direction: Option<OperationDirection>,
idx: usize,
_count: Option<usize>,
) -> usize {
fn right_index(&self, idx: usize) -> usize {
let column_idx = self.column_for_container_idx(idx);
self.first_container_idx(column_idx + 1)
}

View File

@@ -27,20 +27,13 @@ impl From<RECT> for Rect {
}
impl Rect {
/// decrease the size of self by the padding amount.
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.
pub fn add_margin(&mut self, margin: i32) {
self.left -= margin;
self.top -= margin;
self.right += margin * 2;
self.bottom += margin * 2;
pub fn add_padding(&mut self, padding: Option<i32>) {
if let Some(padding) = padding {
self.left += padding;
self.top += padding;
self.right -= padding * 2;
self.bottom -= padding * 2;
}
}
#[must_use]
@@ -50,14 +43,4 @@ impl Rect {
&& point.1 >= self.top
&& point.1 <= self.top + self.bottom
}
#[must_use]
pub const fn scale(&self, system_dpi: i32, rect_dpi: i32) -> Rect {
Rect {
left: (self.left * rect_dpi) / system_dpi,
top: (self.top * rect_dpi) / system_dpi,
right: (self.right * rect_dpi) / system_dpi,
bottom: (self.bottom * rect_dpi) / system_dpi,
}
}
}

View File

@@ -15,13 +15,10 @@ komorebi-core = { path = "../komorebi-core" }
bitflags = "2"
clap = { version = "4", features = ["derive"] }
color-eyre = { workspace = true }
crossbeam-channel = "0.5"
crossbeam-utils = "0.8"
ctrlc = "3"
dirs = { workspace = true }
getset = "0.1"
hex_color = { version = "3", features = ["serde"] }
hotwatch = "0.4"
lazy_static = "1"
miow = "0.5"
@@ -40,13 +37,15 @@ tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uds_windows = "1"
which = "6"
widestring = "1"
windows = { workspace = true }
windows-implement = { workspace = true }
windows-interface = { workspace = true }
which = "5"
winput = "0.2"
winreg = "0.52"
windows-interface = { workspace = true }
windows-implement = { workspace = true }
windows = { workspace = true }
color-eyre = { workspace = true }
dirs = { workspace = true }
widestring = "1"
[features]
deadlock_detection = []

View File

@@ -1,4 +1,5 @@
use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::Result;
use windows::core::PCWSTR;
@@ -11,14 +12,19 @@ use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
use komorebi_core::Rect;
use crate::window::should_act;
use crate::window::Window;
use crate::windows_callbacks;
use crate::WindowsApi;
use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_RECT;
use crate::BORDER_WIDTH;
use crate::REGEX_IDENTIFIERS;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_11;
#[derive(Debug, Clone, Copy)]
pub struct Border {
@@ -62,6 +68,7 @@ impl Border {
unsafe {
while GetMessageW(&mut message, border.hwnd(), 0, 0).into() {
DispatchMessageW(&message);
std::thread::sleep(Duration::from_millis(10));
}
}
@@ -75,6 +82,10 @@ impl Border {
BORDER_HWND.store(hwnd.0, Ordering::SeqCst);
if *WINDOWS_11 {
WindowsApi::round_corners(hwnd.0)?;
}
Ok(())
}
@@ -89,6 +100,7 @@ impl Border {
pub fn set_position(
self,
window: Window,
invisible_borders: &Rect,
activate: bool,
) -> Result<()> {
if self.hwnd == 0 {
@@ -99,10 +111,38 @@ impl Border {
}
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.add_padding(-BORDER_OFFSET.load(Ordering::SeqCst));
rect.top -= invisible_borders.bottom;
rect.bottom += invisible_borders.bottom;
let border_width = BORDER_WIDTH.load(Ordering::SeqCst);
rect.add_margin(border_width);
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let title = &window.title()?;
let exe_name = &window.exe()?;
let class = &window.class()?;
let should_expand_border = should_act(
title,
exe_name,
class,
&border_overflows,
&regex_identifiers,
);
if should_expand_border {
rect.left -= invisible_borders.left;
rect.top -= invisible_borders.top;
rect.right += invisible_borders.right;
rect.bottom += invisible_borders.bottom;
}
let border_offset = BORDER_OFFSET.lock();
if let Some(border_offset) = *border_offset {
rect.left -= border_offset.left;
rect.top -= border_offset.top;
rect.right += border_offset.right;
rect.bottom += border_offset.bottom;
}
*BORDER_RECT.lock() = rect;

View File

@@ -1,103 +0,0 @@
use hex_color::HexColor;
use schemars::gen::SchemaGenerator;
use schemars::schema::InstanceType;
use schemars::schema::Schema;
use schemars::schema::SchemaObject;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum Colour {
/// Colour represented as RGB
Rgb(Rgb),
/// Colour represented as Hex
Hex(Hex),
}
impl From<Rgb> for Colour {
fn from(value: Rgb) -> Self {
Self::Rgb(value)
}
}
impl From<u32> for Colour {
fn from(value: u32) -> Self {
Self::Rgb(Rgb::from(value))
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub struct Hex(HexColor);
impl JsonSchema for Hex {
fn schema_name() -> String {
String::from("Hex")
}
fn json_schema(_: &mut SchemaGenerator) -> Schema {
SchemaObject {
instance_type: Some(InstanceType::String.into()),
..Default::default()
}
.into()
}
}
impl From<Colour> for u32 {
fn from(value: Colour) -> Self {
match value {
Colour::Rgb(val) => val.into(),
Colour::Hex(val) => (Rgb::from(val)).into(),
}
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize, JsonSchema)]
pub struct Rgb {
/// Red
pub r: u32,
/// Green
pub g: u32,
/// Blue
pub b: u32,
}
impl Rgb {
pub fn new(r: u32, g: u32, b: u32) -> Self {
Self { r, g, b }
}
}
impl From<Hex> for Rgb {
fn from(value: Hex) -> Self {
value.0.into()
}
}
impl From<HexColor> for Rgb {
fn from(value: HexColor) -> Self {
Self {
r: value.r as u32,
g: value.g as u32,
b: value.b as u32,
}
}
}
impl From<Rgb> for u32 {
fn from(value: Rgb) -> Self {
value.r | (value.g << 8) | (value.b << 16)
}
}
impl From<u32> for Rgb {
fn from(value: u32) -> Self {
Self {
r: value & 0xff,
g: value >> 8 & 0xff,
b: value >> 16 & 0xff,
}
}
}

View File

@@ -1,4 +1,5 @@
use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::Result;
use windows::core::PCWSTR;
@@ -58,6 +59,7 @@ impl Hidden {
unsafe {
while GetMessageW(&mut message, hidden.hwnd(), 0, 0).into() {
DispatchMessageW(&message);
std::thread::sleep(Duration::from_millis(10));
}
}

View File

@@ -2,7 +2,6 @@ pub mod border;
pub mod com;
#[macro_use]
pub mod ring;
pub mod colour;
pub mod container;
pub mod hidden;
pub mod monitor;
@@ -192,7 +191,8 @@ lazy_static! {
static ref BORDER_RECT: Arc<Mutex<Rect>> =
Arc::new(Mutex::new(Rect::default()));
static ref BORDER_OFFSET: AtomicI32 = Default::default();
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
Arc::new(Mutex::new(None));
// Use app-specific titlebar removal options where possible
// eg. Windows Terminal, IntelliJ IDEA, Firefox

View File

@@ -14,6 +14,8 @@ use std::time::Duration;
use clap::Parser;
use color_eyre::Result;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::Backoff;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
@@ -31,6 +33,7 @@ use komorebi::process_event::listen_for_events;
use komorebi::process_movement::listen_for_movements;
use komorebi::static_config::StaticConfig;
use komorebi::window_manager::WindowManager;
use komorebi::window_manager_event::WindowManagerEvent;
use komorebi::windows_api::WindowsApi;
use komorebi::winevent_listener;
use komorebi::CUSTOM_FFM;
@@ -180,11 +183,15 @@ fn main() -> Result<()> {
WindowsApi::foreground_lock_timeout()?;
winevent_listener::start();
#[cfg(feature = "deadlock_detection")]
detect_deadlocks();
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
crossbeam_channel::unbounded();
let winevent_listener = winevent_listener::new(Arc::new(Mutex::new(outgoing)));
winevent_listener.start();
Hidden::create("komorebi-hidden")?;
let static_config = opts.config.map_or_else(
@@ -207,12 +214,12 @@ fn main() -> Result<()> {
Arc::new(Mutex::new(StaticConfig::preload(
config,
winevent_listener::event_rx(),
Arc::new(Mutex::new(incoming)),
)?))
} else {
Arc::new(Mutex::new(WindowManager::new(
winevent_listener::event_rx(),
)?))
Arc::new(Mutex::new(WindowManager::new(Arc::new(Mutex::new(
incoming,
)))?))
};
wm.lock().init()?;

View File

@@ -195,6 +195,7 @@ impl Monitor {
pub fn update_focused_workspace(
&mut self,
offset: Option<Rect>,
invisible_borders: &Rect,
) -> Result<()> {
let work_area = *self.work_area_size();
let offset = if self.work_area_offset().is_some() {
@@ -205,7 +206,7 @@ impl Monitor {
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?
.update(&work_area, offset)?;
.update(&work_area, offset, invisible_borders)?;
Ok(())
}

View File

@@ -38,7 +38,6 @@ use komorebi_core::WindowContainerBehaviour;
use komorebi_core::WindowKind;
use crate::border::Border;
use crate::colour::Rgb;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::static_config::StaticConfig;
@@ -305,6 +304,7 @@ impl WindowManager {
});
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let mut hwnds_to_purge = vec![];
@@ -347,7 +347,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no focused workspace"))?
.remove_window(hwnd)?;
monitor.update_focused_workspace(offset)?;
monitor.update_focused_workspace(offset, &invisible_borders)?;
}
}
SocketMessage::FocusedWorkspaceContainerPadding(adjustment) => {
@@ -1093,7 +1093,10 @@ impl WindowManager {
SocketMessage::UnmanageFocusedWindow => {
self.unmanage_focused_window()?;
}
SocketMessage::InvisibleBorders(_rect) => {}
SocketMessage::InvisibleBorders(rect) => {
self.invisible_borders = rect;
self.retile_all(false)?;
}
SocketMessage::WorkAreaOffset(rect) => {
self.work_area_offset = Option::from(rect);
self.retile_all(false)?;
@@ -1231,14 +1234,14 @@ impl WindowManager {
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
match kind {
WindowKind::Single => {
BORDER_COLOUR_SINGLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
BORDER_COLOUR_SINGLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
WindowKind::Stack => {
BORDER_COLOUR_STACK.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
BORDER_COLOUR_STACK.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
WindowKind::Monocle => {
BORDER_COLOUR_MONOCLE.store(Rgb::new(r, g, b).into(), Ordering::SeqCst);
BORDER_COLOUR_MONOCLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
}
@@ -1249,7 +1252,17 @@ impl WindowManager {
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::ActiveWindowBorderOffset(offset) => {
BORDER_OFFSET.store(offset, Ordering::SeqCst);
let mut current_border_offset = BORDER_OFFSET.lock();
let new_border_offset = Rect {
left: offset,
top: offset,
right: offset * 2,
bottom: offset * 2,
};
*current_border_offset = Option::from(new_border_offset);
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::AltFocusHack(enable) => {
@@ -1376,6 +1389,9 @@ impl WindowManager {
| SocketMessage::FocusWorkspaceNumber(_) => {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let monocle = BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst);
if monocle != 0 && self.focused_workspace()?.monocle_container().is_some() {
@@ -1392,7 +1408,7 @@ impl WindowManager {
}
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, false)?;
border.set_position(foreground_window, &self.invisible_borders, false)?;
}
SocketMessage::TogglePause => {
let is_paused = self.is_paused;
@@ -1402,7 +1418,7 @@ impl WindowManager {
border.hide()?;
} else {
let focused = self.focused_window()?;
border.set_position(*focused, true)?;
border.set_position(*focused, &self.invisible_borders, true)?;
focused.focus(false)?;
}
}
@@ -1412,7 +1428,7 @@ impl WindowManager {
if tiling_enabled {
let focused = self.focused_window()?;
border.set_position(*focused, true)?;
border.set_position(*focused, &self.invisible_borders, true)?;
focused.focus(false)?;
} else {
border.hide()?;

View File

@@ -35,7 +35,7 @@ use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
#[tracing::instrument]
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
let receiver = wm.lock().incoming_events.clone();
let receiver = wm.lock().incoming_events.lock().clone();
std::thread::spawn(move || {
tracing::info!("listening");
@@ -109,6 +109,7 @@ impl WindowManager {
_ => {}
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
@@ -122,7 +123,7 @@ impl WindowManager {
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
let reaped_orphans = workspace.reap_orphans()?;
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
tracing::info!(
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
reaped_orphans.0,
@@ -302,24 +303,21 @@ impl WindowManager {
}
}
WindowManagerEvent::MoveResizeStart(_, window) => {
if *self.focused_workspace()?.tile() {
let monitor_idx = self.focused_monitor_idx();
let workspace_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace_idx();
let container_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace with this idx"))?
.focused_container_idx();
let monitor_idx = self.focused_monitor_idx();
let workspace_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace_idx();
let container_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace with this idx"))?
.focused_container_idx();
WindowsApi::bring_window_to_top(window.hwnd())?;
WindowsApi::bring_window_to_top(window.hwnd())?;
self.pending_move_op =
Option::from((monitor_idx, workspace_idx, container_idx));
}
self.pending_move_op = Option::from((monitor_idx, workspace_idx, container_idx));
}
WindowManagerEvent::MoveResizeEnd(_, window) => {
// We need this because if the event ends on a different monitor,
@@ -333,6 +331,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("cannot get monitor idx from current position"))?;
let new_window_behaviour = self.window_container_behaviour;
let invisible_borders = self.invisible_borders;
let workspace = self.focused_workspace_mut()?;
if !workspace
@@ -342,7 +341,7 @@ impl WindowManager {
{
let focused_container_idx = workspace.focused_container_idx();
let new_position = WindowsApi::window_rect(window.hwnd())?;
let mut new_position = WindowsApi::window_rect(window.hwnd())?;
let old_position = *workspace
.latest_layout()
@@ -367,6 +366,12 @@ impl WindowManager {
}
}
// Adjust for the invisible borders
new_position.left += invisible_borders.left;
new_position.top += invisible_borders.top;
new_position.right -= invisible_borders.right;
new_position.bottom -= invisible_borders.bottom;
let resize = Rect {
left: new_position.left - old_position.left,
top: new_position.top - old_position.top,
@@ -377,7 +382,9 @@ impl WindowManager {
// If we have moved across the monitors, use that override, otherwise determine
// if a move has taken place by ruling out a resize
let is_move = moved_across_monitors
|| resize.right == 0 && resize.bottom == 0;
|| resize.right == 0 && resize.bottom == 0
|| resize.right.abs() == invisible_borders.right
&& resize.bottom.abs() == invisible_borders.bottom;
if is_move {
tracing::info!("moving with mouse");
@@ -511,12 +518,6 @@ impl WindowManager {
| WindowManagerEvent::Uncloak(..) => {}
};
if !self.focused_workspace()?.tile() {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
BORDER_HIDDEN.store(true, Ordering::SeqCst);
}
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
match event {
WindowManagerEvent::MoveResizeStart(_, _) => {
@@ -528,7 +529,6 @@ impl WindowManager {
| WindowManagerEvent::Show(_, window)
| WindowManagerEvent::FocusChange(_, window)
| WindowManagerEvent::Hide(_, window)
| WindowManagerEvent::Uncloak(_, window)
| WindowManagerEvent::Minimize(_, window) => {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
let mut target_window = None;
@@ -585,10 +585,15 @@ impl WindowManager {
}
if let Some(target_window) = target_window {
let window = target_window;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
border.set_position(target_window, activate)?;
border.set_position(target_window, &self.invisible_borders, activate)?;
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
@@ -601,7 +606,7 @@ impl WindowManager {
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
if let WindowManagerEvent::Unmanage(window) = event {
window.center(&self.focused_monitor_work_area()?)?;
window.center(&self.focused_monitor_work_area()?, &invisible_borders)?;
}
// If there are no more windows on the workspace, we shouldn't show the border window

View File

@@ -1,5 +1,4 @@
use crate::border::Border;
use crate::colour::Colour;
use crate::current_virtual_desktop;
use crate::monitor::Monitor;
use crate::ring::Ring;
@@ -29,7 +28,6 @@ use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REGEX_IDENTIFIERS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
use color_eyre::Result;
use crossbeam_channel::Receiver;
use hotwatch::notify::DebouncedEvent;
@@ -64,14 +62,34 @@ use std::sync::Arc;
use uds_windows::UnixListener;
use uds_windows::UnixStream;
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct Rgb {
/// Red
pub r: u32,
/// Green
pub g: u32,
/// Blue
pub b: u32,
}
impl From<u32> for Rgb {
fn from(value: u32) -> Self {
Self {
r: value & 0xff,
g: value >> 8 & 0xff,
b: value >> 16 & 0xff,
}
}
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ActiveWindowBorderColours {
/// Border colour when the container contains a single window
pub single: Colour,
pub single: Rgb,
/// Border colour when the container contains multiple windows
pub stack: Colour,
pub stack: Rgb,
/// Border colour when the container is in monocle mode
pub monocle: Colour,
pub monocle: Rgb,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
@@ -298,6 +316,13 @@ pub struct StaticConfig {
impl From<&WindowManager> for StaticConfig {
#[allow(clippy::too_many_lines)]
fn from(value: &WindowManager) -> Self {
let default_invisible_borders = Rect {
left: 7,
top: 0,
right: 14,
bottom: 7,
};
let mut monitors = vec![];
for m in value.monitors() {
monitors.push(MonitorConfig::from(m));
@@ -352,13 +377,13 @@ impl From<&WindowManager> for StaticConfig {
None
} else {
Option::from(ActiveWindowBorderColours {
single: Colour::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)),
stack: Colour::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 {
single: Rgb::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)),
stack: Rgb::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 {
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
} else {
BORDER_COLOUR_STACK.load(Ordering::SeqCst)
}),
monocle: Colour::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 {
monocle: Rgb::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 {
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
} else {
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst)
@@ -367,7 +392,11 @@ impl From<&WindowManager> for StaticConfig {
};
Self {
invisible_borders: None,
invisible_borders: if value.invisible_borders == default_invisible_borders {
None
} else {
Option::from(value.invisible_borders)
},
resize_delta: Option::from(value.resize_delta),
window_container_behaviour: Option::from(value.window_container_behaviour),
cross_monitor_move_behaviour: Option::from(value.cross_monitor_move_behaviour),
@@ -378,7 +407,9 @@ impl From<&WindowManager> for StaticConfig {
mouse_follows_focus: Option::from(value.mouse_follows_focus),
app_specific_configuration_path: None,
active_window_border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
active_window_border_offset: Option::from(BORDER_OFFSET.load(Ordering::SeqCst)),
active_window_border_offset: BORDER_OFFSET
.lock()
.map_or(None, |offset| Option::from(offset.left)),
active_window_border: Option::from(BORDER_ENABLED.load(Ordering::SeqCst)),
active_window_border_colours: border_colours,
default_workspace_padding: Option::from(
@@ -431,20 +462,47 @@ impl StaticConfig {
self.active_window_border_width.map_or_else(
|| {
BORDER_WIDTH.store(8, Ordering::SeqCst);
BORDER_WIDTH.store(20, Ordering::SeqCst);
},
|width| {
BORDER_WIDTH.store(width, Ordering::SeqCst);
},
);
self.active_window_border_offset.map_or_else(
|| {
let mut border_offset = BORDER_OFFSET.lock();
*border_offset = None;
},
|offset| {
let new_border_offset = Rect {
left: offset,
top: offset,
right: offset * 2,
bottom: offset * 2,
};
BORDER_OFFSET.store(self.active_window_border_offset.unwrap_or(-1), Ordering::SeqCst);
let mut border_offset = BORDER_OFFSET.lock();
*border_offset = Some(new_border_offset);
},
);
if let Some(colours) = &self.active_window_border_colours {
BORDER_COLOUR_SINGLE.store(u32::from(colours.single), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(u32::from(colours.single), Ordering::SeqCst);
BORDER_COLOUR_STACK.store(u32::from(colours.stack), Ordering::SeqCst);
BORDER_COLOUR_MONOCLE.store(u32::from(colours.monocle), Ordering::SeqCst);
BORDER_COLOUR_SINGLE.store(
colours.single.r | (colours.single.g << 8) | (colours.single.b << 16),
Ordering::SeqCst,
);
BORDER_COLOUR_CURRENT.store(
colours.single.r | (colours.single.g << 8) | (colours.single.b << 16),
Ordering::SeqCst,
);
BORDER_COLOUR_STACK.store(
colours.stack.r | (colours.stack.g << 8) | (colours.stack.b << 16),
Ordering::SeqCst,
);
BORDER_COLOUR_MONOCLE.store(
colours.monocle.r | (colours.monocle.g << 8) | (colours.monocle.b << 16),
Ordering::SeqCst,
);
}
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
@@ -690,7 +748,7 @@ impl StaticConfig {
#[allow(clippy::too_many_lines)]
pub fn preload(
path: &PathBuf,
incoming: Receiver<WindowManagerEvent>,
incoming: Arc<Mutex<Receiver<WindowManagerEvent>>>,
) -> Result<WindowManager> {
let content = std::fs::read_to_string(path)?;
let mut value: Self = serde_json::from_str(&content)?;
@@ -717,6 +775,12 @@ impl StaticConfig {
incoming_events: incoming,
command_listener: listener,
is_paused: false,
invisible_borders: value.invisible_borders.unwrap_or(Rect {
left: 7,
top: 0,
right: 14,
bottom: 7,
}),
virtual_desktop_id: current_virtual_desktop(),
work_area_offset: value.global_work_area_offset,
window_container_behaviour: value
@@ -863,6 +927,10 @@ impl StaticConfig {
wm.hide_border()?;
}
if let Some(val) = value.invisible_borders {
wm.invisible_borders = val;
}
if let Some(val) = value.window_container_behaviour {
wm.window_container_behaviour = val;
}

View File

@@ -32,6 +32,7 @@ use crate::styles::WindowStyle;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDDEN_HWNDS;
use crate::HIDING_BEHAVIOUR;
@@ -128,7 +129,7 @@ impl Window {
HWND(self.hwnd)
}
pub fn center(&mut self, work_area: &Rect) -> Result<()> {
pub fn center(&mut self, work_area: &Rect, invisible_borders: &Rect) -> Result<()> {
let half_width = work_area.right / 2;
let half_weight = work_area.bottom / 2;
@@ -139,6 +140,7 @@ impl Window {
right: half_width,
bottom: half_weight,
},
invisible_borders,
true,
)
}
@@ -146,20 +148,37 @@ impl Window {
pub fn set_position(
&mut self,
layout: &Rect,
invisible_borders: &Rect,
top: bool,
) -> Result<()> {
let rect = *layout;
let mut rect = *layout;
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let title = &self.title()?;
let class = &self.class()?;
let exe_name = &self.exe()?;
let should_remove_border = !should_act(
title,
exe_name,
class,
&border_overflows,
&regex_identifiers,
);
if should_remove_border {
// Remove the invisible borders
rect.left -= invisible_borders.left;
rect.top -= invisible_borders.top;
rect.right += invisible_borders.right;
rect.bottom += invisible_borders.bottom;
}
WindowsApi::position_window(self.hwnd(), &rect, top)
}
pub fn is_maximized(self) -> bool {
WindowsApi::is_zoomed(self.hwnd())
}
pub fn is_miminized(self) -> bool {
WindowsApi::is_iconic(self.hwnd())
}
pub fn hide(self) {
let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
if !programmatically_hidden_hwnds.contains(&self.hwnd) {

View File

@@ -45,7 +45,7 @@ use crate::static_config::StaticConfig;
use crate::window::Window;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent_listener;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
use crate::workspace::Workspace;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
@@ -66,9 +66,10 @@ use crate::WORKSPACE_RULES;
pub struct WindowManager {
pub monitors: Ring<Monitor>,
pub monitor_cache: HashMap<usize, Monitor>,
pub incoming_events: Receiver<WindowManagerEvent>,
pub incoming_events: Arc<Mutex<Receiver<WindowManagerEvent>>>,
pub command_listener: UnixListener,
pub is_paused: bool,
pub invisible_borders: Rect,
pub work_area_offset: Option<Rect>,
pub resize_delta: i32,
pub window_container_behaviour: WindowContainerBehaviour,
@@ -88,6 +89,7 @@ pub struct WindowManager {
pub struct State {
pub monitors: Ring<Monitor>,
pub is_paused: bool,
pub invisible_borders: Rect,
pub resize_delta: i32,
pub new_window_behaviour: WindowContainerBehaviour,
pub cross_monitor_move_behaviour: MoveBehaviour,
@@ -117,6 +119,7 @@ impl From<&WindowManager> for State {
Self {
monitors: wm.monitors.clone(),
is_paused: wm.is_paused,
invisible_borders: wm.invisible_borders,
work_area_offset: wm.work_area_offset,
resize_delta: wm.resize_delta,
new_window_behaviour: wm.window_container_behaviour,
@@ -165,7 +168,7 @@ impl EnforceWorkspaceRuleOp {
impl WindowManager {
#[tracing::instrument]
pub fn new(incoming: Receiver<WindowManagerEvent>) -> Result<Self> {
pub fn new(incoming: Arc<Mutex<Receiver<WindowManagerEvent>>>) -> Result<Self> {
let socket = DATA_DIR.join("komorebi.sock");
match std::fs::remove_file(&socket) {
@@ -187,6 +190,12 @@ impl WindowManager {
incoming_events: incoming,
command_listener: listener,
is_paused: false,
invisible_borders: Rect {
left: 7,
top: 0,
right: 14,
bottom: 7,
},
virtual_desktop_id: current_virtual_desktop(),
work_area_offset: None,
window_container_behaviour: WindowContainerBehaviour::Create,
@@ -213,9 +222,12 @@ impl WindowManager {
pub fn show_border(&self) -> Result<()> {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, true)?;
border.set_position(foreground_window, &self.invisible_borders, true)?;
WindowsApi::invalidate_border_rect()
}
@@ -391,6 +403,7 @@ impl WindowManager {
}
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for monitor in self.monitors_mut() {
@@ -421,7 +434,7 @@ impl WindowManager {
}
if should_update {
monitor.update_focused_workspace(offset)?;
monitor.update_focused_workspace(offset, &invisible_borders)?;
}
}
@@ -623,6 +636,7 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn retile_all(&mut self, preserve_resize_dimensions: bool) -> Result<()> {
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for monitor in self.monitors_mut() {
@@ -644,7 +658,7 @@ impl WindowManager {
}
}
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
}
Ok(())
@@ -654,14 +668,14 @@ impl WindowManager {
pub fn manage_focused_window(&mut self) -> Result<()> {
let hwnd = WindowsApi::foreground_window()?;
let event = WindowManagerEvent::Manage(Window { hwnd });
Ok(winevent_listener::event_tx().send(event)?)
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
}
#[tracing::instrument(skip(self))]
pub fn unmanage_focused_window(&mut self) -> Result<()> {
let hwnd = WindowsApi::foreground_window()?;
let event = WindowManagerEvent::Unmanage(Window { hwnd });
Ok(winevent_listener::event_tx().send(event)?)
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
}
#[tracing::instrument(skip(self))]
@@ -717,7 +731,7 @@ impl WindowManager {
if known_hwnd {
let event = WindowManagerEvent::Raise(Window { hwnd });
self.has_pending_raise_op = true;
Ok(winevent_listener::event_tx().send(event)?)
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
} else {
tracing::debug!("not raising unknown window: {}", Window { hwnd });
Ok(())
@@ -816,11 +830,12 @@ impl WindowManager {
pub fn update_focused_workspace(&mut self, follow_focus: bool) -> Result<()> {
tracing::info!("updating");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
self.focused_monitor_mut()
.ok_or_else(|| anyhow!("there is no monitor"))?
.update_focused_workspace(offset)?;
.update_focused_workspace(offset, &invisible_borders)?;
if follow_focus {
if let Some(window) = self.focused_workspace()?.maximized_window() {
@@ -851,29 +866,6 @@ impl WindowManager {
}
}
// if we passed false for follow_focus
if !follow_focus
// and we have a stack with >1 windows
&& 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()
{
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
if !follow_focus {
if let Some(window) = self.focused_workspace()?.maximized_window() {
window.restore();
window.focus(self.mouse_follows_focus)?;
}
}
Ok(())
}
@@ -1008,12 +1000,13 @@ impl WindowManager {
}
pub fn update_focused_workspace_by_monitor_idx(&mut self, idx: usize) -> Result<()> {
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
self.monitors_mut()
.get_mut(idx)
.ok_or_else(|| anyhow!("there is no monitor"))?
.update_focused_workspace(offset)
.update_focused_workspace(offset, &invisible_borders)
}
#[tracing::instrument(skip(self))]
@@ -1103,6 +1096,7 @@ impl WindowManager {
tracing::info!("moving container");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let mouse_follows_focus = self.mouse_follows_focus;
@@ -1121,7 +1115,7 @@ impl WindowManager {
.remove_focused_container()
.ok_or_else(|| anyhow!("there is no container"))?;
monitor.update_focused_workspace(offset)?;
monitor.update_focused_workspace(offset, &invisible_borders)?;
let target_monitor = self
.monitors_mut()
@@ -1130,7 +1124,7 @@ impl WindowManager {
target_monitor.add_container(container, workspace_idx)?;
target_monitor.load_focused_workspace(mouse_follows_focus)?;
target_monitor.update_focused_workspace(offset)?;
target_monitor.update_focused_workspace(offset, &invisible_borders)?;
if follow {
self.focus_monitor(monitor_idx)?;
@@ -1312,11 +1306,12 @@ impl WindowManager {
// make sure to update the origin monitor workspace layout because it is no
// longer focused so it won't get updated at the end of this fn
let offset = self.work_area_offset;
let invisible_borders = self.invisible_borders;
self.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.update_focused_workspace(offset)?;
.update_focused_workspace(offset, &invisible_borders)?;
let a = self
.focused_monitor()
@@ -1466,15 +1461,9 @@ impl WindowManager {
pub fn promote_container_to_front(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?;
if matches!(workspace.layout(), Layout::Default(DefaultLayout::Grid)) {
tracing::debug!("ignoring promote command for grid layout");
return Ok(());
}
tracing::info!("promoting container");
let workspace = self.focused_workspace_mut()?;
workspace.promote_container()?;
self.update_focused_workspace(self.mouse_follows_focus)
}
@@ -1483,15 +1472,9 @@ impl WindowManager {
pub fn promote_focus_to_front(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?;
if matches!(workspace.layout(), Layout::Default(DefaultLayout::Grid)) {
tracing::info!("ignoring promote focus command for grid layout");
return Ok(());
}
tracing::info!("promoting focus");
let workspace = self.focused_workspace_mut()?;
let target_idx = match workspace.layout() {
Layout::Default(_) => 0,
Layout::Custom(custom) => custom
@@ -1552,6 +1535,7 @@ impl WindowManager {
tracing::info!("floating window");
let work_area = self.focused_monitor_work_area()?;
let invisible_borders = self.invisible_borders;
let workspace = self.focused_workspace_mut()?;
workspace.new_floating_window()?;
@@ -1561,7 +1545,7 @@ impl WindowManager {
.last_mut()
.ok_or_else(|| anyhow!("there is no floating window"))?;
window.center(&work_area)?;
window.center(&work_area, &invisible_borders)?;
window.focus(self.mouse_follows_focus)?;
Ok(())
@@ -1637,10 +1621,10 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn flip_layout(&mut self, layout_flip: Axis) -> Result<()> {
let workspace = self.focused_workspace_mut()?;
tracing::info!("flipping layout");
let workspace = self.focused_workspace_mut()?;
#[allow(clippy::match_same_arms)]
match workspace.layout_flip() {
None => {
@@ -1818,6 +1802,7 @@ impl WindowManager {
) -> Result<()> {
tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
@@ -1846,7 +1831,7 @@ impl WindowManager {
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)
@@ -1866,6 +1851,7 @@ impl WindowManager {
{
tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
@@ -1896,7 +1882,7 @@ impl WindowManager {
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)
@@ -1911,6 +1897,7 @@ impl WindowManager {
) -> Result<()> {
tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
@@ -1937,7 +1924,7 @@ impl WindowManager {
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)
@@ -1953,6 +1940,7 @@ impl WindowManager {
) -> Result<()> {
tracing::info!("setting workspace layout");
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
@@ -1978,7 +1966,7 @@ impl WindowManager {
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)
@@ -1997,6 +1985,7 @@ impl WindowManager {
{
tracing::info!("setting workspace layout");
let layout = CustomLayout::from_path(path)?;
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let focused_monitor_idx = self.focused_monitor_idx();
@@ -2023,7 +2012,7 @@ impl WindowManager {
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
workspace.update(&work_area, offset)?;
workspace.update(&work_area, offset, &invisible_borders)?;
Ok(())
} else {
Ok(self.update_focused_workspace(false)?)

View File

@@ -22,7 +22,6 @@ use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
use windows::Win32::Graphics::Dwm::DWMWA_WINDOW_CORNER_PREFERENCE;
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
@@ -54,8 +53,6 @@ use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use windows::Win32::UI::HiDpi::GetDpiForMonitor;
use windows::Win32::UI::HiDpi::GetDpiForSystem;
use windows::Win32::UI::HiDpi::GetDpiForWindow;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
@@ -85,7 +82,6 @@ use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::IsZoomed;
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
@@ -104,7 +100,7 @@ use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use windows::Win32::UI::WindowsAndMessaging::HWND_BOTTOM;
use windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOP;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
@@ -129,7 +125,8 @@ use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
@@ -342,25 +339,14 @@ impl WindowsApi {
unsafe { MonitorFromPoint(point, MONITOR_DEFAULTTONEAREST) }.0
}
/// position window resizes the target window to the given layout, adjusting
/// the layout to account for any window shadow borders (the window painted
/// region will match layout on completion).
pub fn position_window(hwnd: HWND, layout: &Rect, top: bool) -> Result<()> {
let flags = SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::NO_SEND_CHANGING
| SetWindowPosition::NO_COPY_BITS
| SetWindowPosition::FRAME_CHANGED;
let shadow_rect = Self::shadow_rect(hwnd)?;
let rect = Rect{
left: layout.left + shadow_rect.left,
top: layout.top + shadow_rect.top,
right: layout.right + shadow_rect.right,
bottom: layout.bottom + shadow_rect.bottom,
};
let position = if top { HWND_TOP } else { HWND_BOTTOM };
Self::set_window_pos(hwnd, &rect, position, flags.bits())
let position = if top { HWND_TOPMOST } else { HWND_BOTTOM };
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
pub fn bring_window_to_top(hwnd: HWND) -> Result<()> {
@@ -370,7 +356,7 @@ impl WindowsApi {
pub fn raise_window(hwnd: HWND) -> Result<()> {
let flags = SetWindowPosition::NO_MOVE;
let position = HWND_TOP;
let position = HWND_TOPMOST;
Self::set_window_pos(hwnd, &Rect::default(), position, flags.bits())
}
@@ -381,18 +367,6 @@ impl WindowsApi {
SetWindowPosition::NO_ACTIVATE
};
// TODO(raggi): This leaves the window behind the active window, which
// can result e.g. single pixel window borders being invisible in the
// case of opaque window borders (e.g. EPIC Games Launcher). Ideally
// we'd be able to pass a parent window to place ourselves just in front
// of, however the SetWindowPos API explicitly ignores that parameter
// unless the window being positioned is being activated - and we don't
// want to activate the border window here. We can hopefully find a
// better workaround in the future.
// The trade-off chosen prevents the border window from sitting over the
// top of other pop-up dialogs such as a file picker dialog from
// Firefox. When adjusting this in the future, it's important to check
// those dialog cases.
let position = HWND_NOTOPMOST;
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
@@ -404,8 +378,7 @@ impl WindowsApi {
Self::set_window_pos(hwnd, &Rect::default(), position, flags.bits())
}
/// set_window_pos calls SetWindowPos without any accounting for Window decorations.
fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
pub fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
unsafe {
SetWindowPos(
hwnd,
@@ -497,34 +470,9 @@ impl WindowsApi {
pub fn window_rect(hwnd: HWND) -> Result<Rect> {
let mut rect = unsafe { std::mem::zeroed() };
unsafe { GetWindowRect(hwnd, &mut rect) }.process()?;
if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_ok() {
let window_scale = unsafe { GetDpiForWindow(hwnd) };
let system_scale = unsafe { GetDpiForSystem() };
Ok(Rect::from(rect).scale(system_scale.try_into()?, window_scale.try_into()?))
} else {
unsafe { GetWindowRect(hwnd, &mut rect) }.process()?;
Ok(Rect::from(rect))
}
}
/// shadow_rect computes the offset of the shadow position of the window to
/// the window painted region. The four values in the returned Rect can be
/// added to a position rect to compute a size for set_window_pos that will
/// fill the target area, ignoring shadows.
fn shadow_rect(hwnd: HWND) -> Result<Rect> {
let window_rect = Self::window_rect(hwnd)?;
let mut srect = Default::default();
unsafe { GetWindowRect(hwnd, &mut srect) }.process()?;
let shadow_rect = Rect::from(srect);
Ok(Rect {
left: shadow_rect.left - window_rect.left,
top: shadow_rect.top - window_rect.top,
right: shadow_rect.right - window_rect.right,
bottom: shadow_rect.bottom - window_rect.bottom,
})
Ok(Rect::from(rect))
}
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
@@ -729,10 +677,6 @@ impl WindowsApi {
unsafe { IsIconic(hwnd) }.into()
}
pub fn is_zoomed(hwnd: HWND) -> bool {
unsafe { IsZoomed(hwnd) }.into()
}
pub fn monitor_info_w(hmonitor: HMONITOR) -> Result<MONITORINFOEXW> {
let mut ex_info = MONITORINFOEXW::default();
ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;
@@ -900,10 +844,10 @@ impl WindowsApi {
pub fn create_border_window(name: PCWSTR, instance: HMODULE) -> Result<isize> {
unsafe {
let hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
name,
name,
WS_POPUP | WS_SYSMENU,
WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,

View File

@@ -13,13 +13,11 @@ use windows::Win32::Graphics::Gdi::BeginPaint;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::EndPaint;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::RoundRect;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
use windows::Win32::Graphics::Gdi::PS_INSIDEFRAME;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
@@ -39,14 +37,13 @@ use crate::ring::Ring;
use crate::window::Window;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent_listener;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
use crate::BORDER_WIDTH;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_11;
pub extern "system" fn valid_display_monitors(
hmonitor: HMONITOR,
@@ -149,17 +146,12 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
let is_visible = WindowsApi::is_window_visible(hwnd);
let is_window = WindowsApi::is_window(hwnd);
let is_minimized = WindowsApi::is_iconic(hwnd);
let is_maximized = WindowsApi::is_zoomed(hwnd);
if is_visible && is_window && !is_minimized {
let window = Window { hwnd: hwnd.0 };
if let Ok(should_manage) = window.should_manage(None) {
if should_manage {
if is_maximized {
WindowsApi::restore_window(hwnd);
}
let mut container = Container::default();
container.windows_mut().push_back(window);
containers.push_back(container);
@@ -194,9 +186,11 @@ pub extern "system" fn win_event_hook(
if let Ok(should_manage) = window.should_manage(Option::from(event_type)) {
if should_manage {
winevent_listener::event_tx()
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on winevent_listener::event_tx");
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
}
}
}
@@ -214,7 +208,7 @@ pub extern "system" fn border_window(
let mut ps = PAINTSTRUCT::default();
let hdc = BeginPaint(window, &mut ps);
let hpen = CreatePen(
PS_SOLID | PS_INSIDEFRAME,
PS_SOLID,
BORDER_WIDTH.load(Ordering::SeqCst),
COLORREF(BORDER_COLOUR_CURRENT.load(Ordering::SeqCst)),
);
@@ -222,17 +216,7 @@ pub extern "system" fn border_window(
SelectObject(hdc, hpen);
SelectObject(hdc, hbrush);
// TODO(raggi): this is approximately the correct curvature for
// the top left of a Windows 11 window (DWMWCP_DEFAULT), but
// often the bottom right has a different shape. Furthermore if
// 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.
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);
}
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
EndPaint(window, &ps);
ValidateRect(window, None);
@@ -257,9 +241,11 @@ pub extern "system" fn hidden_window(
match message {
WM_DISPLAYCHANGE => {
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
winevent_listener::event_tx()
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on winevent_listener::event_tx");
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
LRESULT(0)
}
@@ -270,9 +256,11 @@ pub extern "system" fn hidden_window(
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
{
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
winevent_listener::event_tx()
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on winevent_listener::event_tx");
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
}
LRESULT(0)
}
@@ -281,9 +269,11 @@ pub extern "system" fn hidden_window(
#[allow(clippy::cast_possible_truncation)]
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
winevent_listener::event_tx()
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on winevent_listener::event_tx");
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
}
LRESULT(0)
}

View File

@@ -1,62 +1,105 @@
use std::sync::OnceLock;
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::Accessibility::SetWinEventHook;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::PeekMessageW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MAX;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MIN;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::PM_REMOVE;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_callbacks;
static CHANNEL: OnceLock<(Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>)> =
OnceLock::new();
lazy_static! {
pub static ref WINEVENT_CALLBACK_CHANNEL: Arc<Mutex<(Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>)>> =
Arc::new(Mutex::new(crossbeam_channel::unbounded()));
}
static EVENT_PUMP: OnceLock<std::thread::JoinHandle<()>> = OnceLock::new();
#[derive(Debug, Clone)]
pub struct WinEventListener {
hook: Arc<AtomicIsize>,
outgoing_events: Arc<Mutex<Sender<WindowManagerEvent>>>,
}
pub fn start() {
EVENT_PUMP.get_or_init(|| {
std::thread::spawn(move || {
pub fn new(outgoing: Arc<Mutex<Sender<WindowManagerEvent>>>) -> WinEventListener {
WinEventListener {
hook: Arc::new(AtomicIsize::new(0)),
outgoing_events: outgoing,
}
}
impl WinEventListener {
pub fn start(self) {
let hook = self.hook.clone();
let outgoing = self.outgoing_events.lock().clone();
std::thread::spawn(move || unsafe {
let hook_ref = SetWinEventHook(
EVENT_MIN,
EVENT_MAX,
None,
Some(windows_callbacks::win_event_hook),
0,
0,
0,
);
hook.store(hook_ref.0, Ordering::SeqCst);
// The code in the callback doesn't work in its own loop, needs to be within
// the MessageLoop callback for the winevent callback to even fire
MessageLoop::start(10, |_msg| {
if let Ok(event) = WINEVENT_CALLBACK_CHANNEL.lock().1.try_recv() {
match outgoing.send(event) {
Ok(()) => {}
Err(error) => {
tracing::error!("{}", error);
}
}
}
true
});
});
}
}
#[derive(Debug, Copy, Clone)]
pub struct MessageLoop;
impl MessageLoop {
pub fn start(sleep: u64, cb: impl Fn(Option<MSG>) -> bool) {
Self::start_with_sleep(sleep, cb);
}
fn start_with_sleep(sleep: u64, cb: impl Fn(Option<MSG>) -> bool) {
let mut msg: MSG = MSG::default();
loop {
let mut value: Option<MSG> = None;
unsafe {
SetWinEventHook(
EVENT_MIN,
EVENT_MAX,
None,
Some(windows_callbacks::win_event_hook),
0,
0,
0,
)
};
loop {
let mut msg: MSG = MSG::default();
unsafe {
if !GetMessageW(&mut msg, HWND(0), 0, 0).as_bool() {
tracing::info!("windows event processing shutdown");
break;
};
if !bool::from(!PeekMessageW(&mut msg, HWND(0), 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
value = Some(msg);
}
}
})
});
}
fn channel() -> &'static (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) {
CHANNEL.get_or_init(crossbeam_channel::unbounded)
}
std::thread::sleep(Duration::from_millis(sleep));
pub fn event_tx() -> Sender<WindowManagerEvent> {
channel().0.clone()
}
pub fn event_rx() -> Receiver<WindowManagerEvent> {
channel().1.clone()
if !cb(value) {
break;
}
}
}
}

View File

@@ -26,8 +26,6 @@ use crate::static_config::WorkspaceConfig;
use crate::window::Window;
use crate::window::WindowDetails;
use crate::windows_api::WindowsApi;
use crate::BORDER_OFFSET;
use crate::BORDER_WIDTH;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::INITIAL_CONFIGURATION_LOADED;
@@ -207,6 +205,7 @@ impl Workspace {
&mut self,
work_area: &Rect,
offset: Option<Rect>,
invisible_borders: &Rect,
) -> Result<()> {
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
return Ok(());
@@ -226,7 +225,7 @@ impl Workspace {
},
);
adjusted_work_area.add_padding(self.workspace_padding().unwrap_or_default());
adjusted_work_area.add_padding(self.workspace_padding());
self.enforce_resize_constraints();
@@ -248,19 +247,11 @@ impl Workspace {
}
}
let managed_maximized_window = self.maximized_window().is_some();
if *self.tile() {
if let Some(container) = self.monocle_container_mut() {
if let Some(window) = container.focused_window_mut() {
adjusted_work_area.add_padding(container_padding.unwrap_or_default());
{
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
adjusted_work_area.add_padding(border_offset);
let width = BORDER_WIDTH.load(Ordering::SeqCst);
adjusted_work_area.add_padding(width);
}
window.set_position(&adjusted_work_area, true)?;
adjusted_work_area.add_padding(container_padding);
window.set_position(&adjusted_work_area, invisible_borders, true)?;
};
} else if let Some(window) = self.maximized_window_mut() {
window.maximize();
@@ -289,22 +280,7 @@ impl Workspace {
window.add_title_bar()?;
}
// If a window has been unmaximized via toggle-maximize, this block
// will make sure that it is unmaximized via restore_window
if window.is_maximized() && !managed_maximized_window {
WindowsApi::restore_window(window.hwnd());
}
let mut rect = *layout;
{
let border_offset = BORDER_OFFSET.load(Ordering::SeqCst);
rect.add_padding(border_offset);
let width = BORDER_WIDTH.load(Ordering::SeqCst);
rect.add_padding(width);
}
window.set_position(&rect, false)?;
window.set_position(layout, invisible_borders, false)?;
}
}

View File

@@ -31,5 +31,5 @@ serde_yaml = "0.9"
sysinfo = "0.30"
thiserror = "1"
uds_windows = "1"
which = "6"
which = "5"
windows = { workspace = true }

View File

@@ -805,14 +805,8 @@ enum SubCommand {
Start(Start),
/// Stop the komorebi.exe process and restore all hidden windows
Stop(Stop),
/// Check komorebi configuration and related files for common errors
/// Output various important komorebi-related environment values
Check,
/// Show the path to komorebi.json
#[clap(alias = "config")]
Configuration,
/// Show the path to whkdrc
#[clap(alias = "whkd")]
Whkdrc,
/// Show a JSON representation of the current window manager state
State,
/// Show a JSON representation of visible windows
@@ -1187,9 +1181,15 @@ enum SubCommand {
pub fn send_message(bytes: &[u8]) -> Result<()> {
let socket = DATA_DIR.join("komorebi.sock");
let mut stream = UnixStream::connect(socket)?;
stream.write_all(bytes)?;
Ok(stream.shutdown(Shutdown::Write)?)
let mut connected = false;
while !connected {
if let Ok(mut stream) = UnixStream::connect(&socket) {
connected = true;
stream.write_all(bytes)?;
}
}
Ok(())
}
pub fn send_query(bytes: &[u8]) -> Result<String> {
@@ -1420,20 +1420,6 @@ fn main() -> Result<()> {
println!("If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n");
}
}
SubCommand::Configuration => {
let static_config = HOME_DIR.join("komorebi.json");
if static_config.exists() {
println!("{}", static_config.display());
}
}
SubCommand::Whkdrc => {
let whkdrc = WHKD_CONFIG_DIR.join("whkdrc");
if whkdrc.exists() {
println!("{}", whkdrc.display());
}
}
SubCommand::AhkLibrary => {
let library = HOME_DIR.join("komorebic.lib.ahk");
let mut file = OpenOptions::new()

View File

@@ -19,127 +19,100 @@
"properties": {
"monocle": {
"description": "Border colour when the container is in monocle mode",
"anyOf": [
{
"description": "Colour represented as RGB",
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
{
"description": "Colour represented as Hex",
"type": "string"
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
]
}
},
"single": {
"description": "Border colour when the container contains a single window",
"anyOf": [
{
"description": "Colour represented as RGB",
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
{
"description": "Colour represented as Hex",
"type": "string"
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
]
}
},
"stack": {
"description": "Border colour when the container contains multiple windows",
"anyOf": [
{
"description": "Colour represented as RGB",
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
"type": "object",
"required": [
"b",
"g",
"r"
],
"properties": {
"b": {
"description": "Blue",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
{
"description": "Colour represented as Hex",
"type": "string"
"g": {
"description": "Green",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"r": {
"description": "Red",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
]
}
}
}
},
"active_window_border_offset": {
"description": "Offset of the active window border (default: -1)",
"description": "Offset of the active window border (default: None)",
"type": "integer",
"format": "int32"
},
"active_window_border_width": {
"description": "Width of the active window border (default: 8)",
"description": "Width of the active window border (default: 20)",
"type": "integer",
"format": "int32"
},