mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-02-01 22:14:36 +01:00
Compare commits
28 Commits
animate-wi
...
hotfix/log
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c83ea24666 | ||
|
|
21a5be0404 | ||
|
|
7cb2965969 | ||
|
|
d6e83e1778 | ||
|
|
8f30612220 | ||
|
|
f3e41490b2 | ||
|
|
5a6dcef7ea | ||
|
|
0e96cd65b8 | ||
|
|
025cb08b3e | ||
|
|
f9c69e51aa | ||
|
|
8a7c75b9a7 | ||
|
|
ed3b053323 | ||
|
|
a1a7e6c2bf | ||
|
|
b4ae043b9c | ||
|
|
d2a06a11ac | ||
|
|
e6bf30b567 | ||
|
|
326d5bae42 | ||
|
|
d23e3e7c51 | ||
|
|
0eeba6cd0e | ||
|
|
cf86b2cf98 | ||
|
|
e221d96785 | ||
|
|
d3bc78097c | ||
|
|
657ac441ae | ||
|
|
0696a00bd3 | ||
|
|
5111dbdfb9 | ||
|
|
dd12f0fc8a | ||
|
|
76a8695218 | ||
|
|
ddfcf8b76f |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1 +1,2 @@
|
||||
github: LGUG2Z
|
||||
ko_fi: lgug2z
|
||||
|
||||
2
.github/workflows/windows.yaml
vendored
2
.github/workflows/windows.yaml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
cargo install cargo-wix
|
||||
cargo wix -p komorebi --nocapture -I .\wix\main.wxs --target x86_64-pc-windows-msvc
|
||||
- name: Upload the built artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: komorebi-${{ matrix.target }}
|
||||
path: |
|
||||
|
||||
197
Cargo.lock
generated
197
Cargo.lock
generated
@@ -95,6 +95,15 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace-ext"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
@@ -148,9 +157,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.10"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41fffed7514f420abec6d183b1d3acfd9099c79c3a10a06ade4f8203f1411272"
|
||||
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -158,15 +167,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.9"
|
||||
version = "4.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63361bae7eef3771745f02d8d892bec2fee5f6e34af316ba556e7f97a7069ff1"
|
||||
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
"terminal_size",
|
||||
"terminal_size 0.3.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -238,9 +247,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.8"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
|
||||
checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
@@ -272,21 +281,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.16"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.4.1"
|
||||
version = "3.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf"
|
||||
checksum = "b467862cc8610ca6fc9a1532d7777cee0804e678ab45410897b9396495994a0b"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -744,6 +753,23 @@ version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
@@ -801,6 +827,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"uds_windows",
|
||||
"which",
|
||||
"widestring",
|
||||
"windows",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
@@ -837,6 +864,7 @@ dependencies = [
|
||||
"heck",
|
||||
"komorebi-core",
|
||||
"lazy_static",
|
||||
"miette",
|
||||
"paste",
|
||||
"powershell_script",
|
||||
"reqwest",
|
||||
@@ -844,6 +872,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
"uds_windows",
|
||||
"which",
|
||||
"windows",
|
||||
@@ -928,6 +957,38 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"backtrace-ext",
|
||||
"is-terminal",
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"supports-color",
|
||||
"supports-hyperlinks",
|
||||
"supports-unicode",
|
||||
"terminal_size 0.1.17",
|
||||
"textwrap",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
@@ -1300,18 +1361,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1635,9 +1696,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.27"
|
||||
version = "0.9.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c"
|
||||
checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -1670,6 +1731,12 @@ version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.10"
|
||||
@@ -1718,6 +1785,34 @@ dependencies = [
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-hyperlinks"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-unicode"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
@@ -1742,9 +1837,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.29.11"
|
||||
version = "0.30.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666"
|
||||
checksum = "1fb4f3438c8f6389c864e61221cbc97e9bca98b4daf39a5beb7bea660f528bb2"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"core-foundation-sys",
|
||||
@@ -1752,7 +1847,7 @@ dependencies = [
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"winapi 0.3.9",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1789,6 +1884,16 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminal_size"
|
||||
version = "0.3.0"
|
||||
@@ -1800,19 +1905,30 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
name = "textwrap"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
|
||||
checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.50"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
|
||||
checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2020,10 +2136,11 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "uds_windows"
|
||||
version = "1.0.2"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d"
|
||||
checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"tempfile",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
@@ -2040,6 +2157,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.22"
|
||||
@@ -2050,10 +2173,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.9"
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
@@ -2204,6 +2333,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
Tiling Window Management for Windows.
|
||||
|
||||
<p>
|
||||
<a href="https://techforpalestine.org/learn-more">
|
||||
<img alt="Tech for Palestine" src="https://badge.techforpalestine.org/default">
|
||||
</a>
|
||||
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/LGUG2Z/komorebi/.github/workflows/windows.yaml">
|
||||
<img alt="GitHub" src="https://img.shields.io/github/license/LGUG2Z/komorebi">
|
||||
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/LGUG2Z/komorebi/total">
|
||||
|
||||
2
justfile
2
justfile
@@ -28,7 +28,7 @@ install:
|
||||
just install-target komorebi
|
||||
|
||||
run:
|
||||
just install-komorebic
|
||||
just install-target komorebic
|
||||
cargo +stable run --bin komorebi --locked
|
||||
|
||||
warn $RUST_LOG="warn":
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
use clap::ValueEnum;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
#[derive(
|
||||
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
|
||||
)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum EaseEnum {
|
||||
Linear,
|
||||
EaseInSine,
|
||||
EaseOutSine,
|
||||
EaseInOutSine,
|
||||
EaseInQuad,
|
||||
EaseOutQuad,
|
||||
EaseInOutQuad,
|
||||
EaseInCubic,
|
||||
EaseInOutCubic,
|
||||
EaseInQuart,
|
||||
EaseOutQuart,
|
||||
EaseInOutQuart,
|
||||
EaseInQuint,
|
||||
EaseOutQuint,
|
||||
EaseInOutQuint,
|
||||
EaseInExpo,
|
||||
EaseOutExpo,
|
||||
EaseInOutExpo,
|
||||
EaseInCirc,
|
||||
EaseOutCirc,
|
||||
EaseInOutCirc,
|
||||
EaseInBack,
|
||||
EaseOutBack,
|
||||
EaseInOutBack,
|
||||
EaseInElastic,
|
||||
EaseOutElastic,
|
||||
EaseInOutElastic,
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
EaseInOutBounce,
|
||||
}
|
||||
@@ -14,7 +14,6 @@ use serde::Serialize;
|
||||
use strum::Display;
|
||||
use strum::EnumString;
|
||||
|
||||
pub use animation::EaseEnum;
|
||||
pub use arrangement::Arrangement;
|
||||
pub use arrangement::Axis;
|
||||
pub use custom_layout::CustomLayout;
|
||||
@@ -25,7 +24,6 @@ pub use layout::Layout;
|
||||
pub use operation_direction::OperationDirection;
|
||||
pub use rect::Rect;
|
||||
|
||||
pub mod animation;
|
||||
pub mod arrangement;
|
||||
pub mod config_generation;
|
||||
pub mod custom_layout;
|
||||
@@ -86,6 +84,7 @@ pub enum SocketMessage {
|
||||
FlipLayout(Axis),
|
||||
// Monitor and Workspace Commands
|
||||
MonitorIndexPreference(usize, i32, i32, i32, i32),
|
||||
DisplayIndexPreference(usize, String),
|
||||
EnsureWorkspaces(usize, usize),
|
||||
EnsureNamedWorkspaces(usize, Vec<String>),
|
||||
NewWorkspace,
|
||||
@@ -100,6 +99,7 @@ pub enum SocketMessage {
|
||||
CycleFocusMonitor(CycleDirection),
|
||||
CycleFocusWorkspace(CycleDirection),
|
||||
FocusMonitorNumber(usize),
|
||||
FocusLastWorkspace,
|
||||
FocusWorkspaceNumber(usize),
|
||||
FocusWorkspaceNumbers(usize),
|
||||
FocusMonitorWorkspaceNumber(usize, usize),
|
||||
@@ -129,9 +129,6 @@ pub enum SocketMessage {
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
AltFocusHack(bool),
|
||||
Animation(bool),
|
||||
AnimationDuration(u64),
|
||||
AnimationEase(EaseEnum),
|
||||
ActiveWindowBorder(bool),
|
||||
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
||||
ActiveWindowBorderWidth(i32),
|
||||
@@ -151,6 +148,7 @@ pub enum SocketMessage {
|
||||
IdentifyLayeredApplication(ApplicationIdentifier, String),
|
||||
IdentifyBorderOverflowApplication(ApplicationIdentifier, String),
|
||||
State,
|
||||
VisibleWindows,
|
||||
Query(StateQuery),
|
||||
FocusFollowsMouse(FocusFollowsMouseImplementation, bool),
|
||||
ToggleFocusFollowsMouse(FocusFollowsMouseImplementation),
|
||||
|
||||
@@ -32,7 +32,7 @@ schemars = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
strum = { version = "0.25", features = ["derive"] }
|
||||
sysinfo = "0.29"
|
||||
sysinfo = "0.30"
|
||||
tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
@@ -45,6 +45,7 @@ windows-implement = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
widestring = "1"
|
||||
|
||||
[features]
|
||||
deadlock_detection = []
|
||||
|
||||
@@ -1,510 +0,0 @@
|
||||
use color_eyre::Result;
|
||||
use komorebi_core::EaseEnum;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use schemars::JsonSchema;
|
||||
|
||||
use std::f64::consts::PI;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::ANIMATION_EASE;
|
||||
|
||||
pub trait Ease {
|
||||
fn evaluate(t: f64) -> f64;
|
||||
}
|
||||
|
||||
pub struct Linear;
|
||||
|
||||
impl Ease for Linear {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInSine;
|
||||
|
||||
impl Ease for EaseInSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::cos((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutSine;
|
||||
|
||||
impl Ease for EaseOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sin((t * PI) / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutSine;
|
||||
|
||||
impl Ease for EaseInOutSine {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
-(f64::cos(PI * t) - 1.0) / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuad;
|
||||
|
||||
impl Ease for EaseInQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuad;
|
||||
|
||||
impl Ease for EaseOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
(1.0 - t).mul_add(-1.0 - t, 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuad;
|
||||
|
||||
impl Ease for EaseInOutQuad {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
2.0 * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(2) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCubic;
|
||||
|
||||
impl Ease for EaseInCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCubic;
|
||||
|
||||
impl Ease for EaseOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(3)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCubic;
|
||||
|
||||
impl Ease for EaseInOutCubic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
4.0 * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(3) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuart;
|
||||
|
||||
impl Ease for EaseInQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuart;
|
||||
|
||||
impl Ease for EaseOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuart;
|
||||
|
||||
impl Ease for EaseInOutQuart {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
8.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(4) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInQuint;
|
||||
|
||||
impl Ease for EaseInQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
t * t * t * t * t
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutQuint;
|
||||
|
||||
impl Ease for EaseOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - (1.0 - t).powi(5)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutQuint;
|
||||
|
||||
impl Ease for EaseInOutQuint {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
16.0 * t * t * t * t
|
||||
} else {
|
||||
1.0 - (-2.0f64).mul_add(t, 2.0).powi(5) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInExpo;
|
||||
|
||||
impl Ease for EaseInExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
10.0f64.mul_add(t, -10.0).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutExpo;
|
||||
|
||||
impl Ease for EaseOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
1.0 - (-10.0 * t).exp2()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutExpo;
|
||||
|
||||
impl Ease for EaseInOutExpo {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t == 0.0 || (t - 1.0).abs() < f64::EPSILON {
|
||||
return t;
|
||||
}
|
||||
|
||||
if t < 0.5 {
|
||||
20.0f64.mul_add(t, -10.0).exp2() / 2.0
|
||||
} else {
|
||||
(2.0 - (-20.0f64).mul_add(t, 10.0).exp2()) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInCirc;
|
||||
|
||||
impl Ease for EaseInCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - f64::sqrt(t.mul_add(-t, 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutCirc;
|
||||
|
||||
impl Ease for EaseOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
f64::sqrt((t - 1.0).mul_add(-(t - 1.0), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutCirc;
|
||||
|
||||
impl Ease for EaseInOutCirc {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - f64::sqrt((2.0 * t).mul_add(-(2.0 * t), 1.0))) / 2.0
|
||||
} else {
|
||||
(f64::sqrt(
|
||||
(-2.0f64)
|
||||
.mul_add(t, 2.0)
|
||||
.mul_add(-(-2.0f64).mul_add(t, 2.0), 1.0),
|
||||
) + 1.0)
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBack;
|
||||
|
||||
impl Ease for EaseInBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1 = 1.70158;
|
||||
let c3 = c1 + 1.0;
|
||||
|
||||
(c3 * t * t).mul_add(t, -c1 * t * t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBack;
|
||||
|
||||
impl Ease for EaseOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c3: f64 = c1 + 1.0;
|
||||
|
||||
c1.mul_add((t - 1.0).powi(2), c3.mul_add((t - 1.0).powi(3), 1.0))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBack;
|
||||
|
||||
impl Ease for EaseInOutBack {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let c1: f64 = 1.70158;
|
||||
let c2: f64 = c1 * 1.525;
|
||||
|
||||
if t < 0.5 {
|
||||
((2.0 * t).powi(2) * ((c2 + 1.0) * 2.0).mul_add(t, -c2)) / 2.0
|
||||
} else {
|
||||
((2.0f64.mul_add(t, -2.0))
|
||||
.powi(2)
|
||||
.mul_add((c2 + 1.0).mul_add(t.mul_add(2.0, -2.0), c2), 2.0))
|
||||
/ 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInElastic;
|
||||
|
||||
impl Ease for EaseInElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
-(10.0f64.mul_add(t, -10.0).exp2()) * f64::sin(t.mul_add(10.0, -10.75) * c4)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutElastic;
|
||||
|
||||
impl Ease for EaseOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c4 = (2.0 * PI) / 3.0;
|
||||
|
||||
(-10.0 * t)
|
||||
.exp2()
|
||||
.mul_add(f64::sin(t.mul_add(10.0, -0.75) * c4), 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutElastic;
|
||||
|
||||
impl Ease for EaseInOutElastic {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if (t - 1.0).abs() < f64::EPSILON || t == 0.0 {
|
||||
return t;
|
||||
}
|
||||
|
||||
let c5 = (2.0 * PI) / 4.5;
|
||||
|
||||
if t < 0.5 {
|
||||
-(20.0f64.mul_add(t, -10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
} else {
|
||||
((-20.0f64).mul_add(t, 10.0).exp2() * f64::sin(20.0f64.mul_add(t, -11.125) * c5)) / 2.0
|
||||
+ 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInBounce;
|
||||
|
||||
impl Ease for EaseInBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
1.0 - EaseOutBounce::evaluate(1.0 - t)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseOutBounce;
|
||||
|
||||
impl Ease for EaseOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
let mut time = t;
|
||||
let n1 = 7.5625;
|
||||
let d1 = 2.75;
|
||||
|
||||
if t < 1.0 / d1 {
|
||||
n1 * time * time
|
||||
} else if time < 2.0 / d1 {
|
||||
time -= 1.5 / d1;
|
||||
(n1 * time).mul_add(time, 0.75)
|
||||
} else if time < 2.5 / d1 {
|
||||
time -= 2.25 / d1;
|
||||
(n1 * time).mul_add(time, 0.9375)
|
||||
} else {
|
||||
time -= 2.625 / d1;
|
||||
(n1 * time).mul_add(time, 0.984_375)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EaseInOutBounce;
|
||||
|
||||
impl Ease for EaseInOutBounce {
|
||||
fn evaluate(t: f64) -> f64 {
|
||||
if t < 0.5 {
|
||||
(1.0 - EaseOutBounce::evaluate(2.0f64.mul_add(-t, 1.0))) / 2.0
|
||||
} else {
|
||||
(1.0 + EaseOutBounce::evaluate(2.0f64.mul_add(t, -1.0))) / 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
fn apply_ease_func(t: f64) -> f64 {
|
||||
let ease = *ANIMATION_EASE.lock();
|
||||
|
||||
match ease {
|
||||
EaseEnum::Linear => Linear::evaluate(t),
|
||||
EaseEnum::EaseInSine => EaseInSine::evaluate(t),
|
||||
EaseEnum::EaseOutSine => EaseOutSine::evaluate(t),
|
||||
EaseEnum::EaseInOutSine => EaseInOutSine::evaluate(t),
|
||||
EaseEnum::EaseInQuad => EaseInQuad::evaluate(t),
|
||||
EaseEnum::EaseOutQuad => EaseOutQuad::evaluate(t),
|
||||
EaseEnum::EaseInOutQuad => EaseInOutQuad::evaluate(t),
|
||||
EaseEnum::EaseInCubic => EaseInCubic::evaluate(t),
|
||||
EaseEnum::EaseInOutCubic => EaseInOutCubic::evaluate(t),
|
||||
EaseEnum::EaseInQuart => EaseInQuart::evaluate(t),
|
||||
EaseEnum::EaseOutQuart => EaseOutQuart::evaluate(t),
|
||||
EaseEnum::EaseInOutQuart => EaseInOutQuart::evaluate(t),
|
||||
EaseEnum::EaseInQuint => EaseInQuint::evaluate(t),
|
||||
EaseEnum::EaseOutQuint => EaseOutQuint::evaluate(t),
|
||||
EaseEnum::EaseInOutQuint => EaseInOutQuint::evaluate(t),
|
||||
EaseEnum::EaseInExpo => EaseInExpo::evaluate(t),
|
||||
EaseEnum::EaseOutExpo => EaseOutExpo::evaluate(t),
|
||||
EaseEnum::EaseInOutExpo => EaseInOutExpo::evaluate(t),
|
||||
EaseEnum::EaseInCirc => EaseInCirc::evaluate(t),
|
||||
EaseEnum::EaseOutCirc => EaseOutCirc::evaluate(t),
|
||||
EaseEnum::EaseInOutCirc => EaseInOutCirc::evaluate(t),
|
||||
EaseEnum::EaseInBack => EaseInBack::evaluate(t),
|
||||
EaseEnum::EaseOutBack => EaseOutBack::evaluate(t),
|
||||
EaseEnum::EaseInOutBack => EaseInOutBack::evaluate(t),
|
||||
EaseEnum::EaseInElastic => EaseInElastic::evaluate(t),
|
||||
EaseEnum::EaseOutElastic => EaseOutElastic::evaluate(t),
|
||||
EaseEnum::EaseInOutElastic => EaseInOutElastic::evaluate(t),
|
||||
EaseEnum::EaseInBounce => EaseInBounce::evaluate(t),
|
||||
EaseEnum::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||
EaseEnum::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy, JsonSchema)]
|
||||
pub struct Animation {
|
||||
// is_cancel: AtomicBool,
|
||||
// pub in_progress: AtomicBool,
|
||||
is_cancel: bool,
|
||||
pub in_progress: bool,
|
||||
}
|
||||
|
||||
// impl Default for Animation {
|
||||
// fn default() -> Self {
|
||||
// Animation {
|
||||
// // I'm not sure if this is the right way to do it
|
||||
// // I've tried to use Arc<Mutex<bool>> but it dooes not implement Copy trait
|
||||
// // and I dont want to rewrite everything cause I'm not experienced with rust
|
||||
// // Down here you can see the idea I've tried to achive like in any other OOP language
|
||||
// // My thought is that in order to prevent Google Chrome breaking render window
|
||||
// // I need to cancel animation if user starting new window movement. So window stops
|
||||
// // moving at one point and then fires new animation.
|
||||
// // But my approach does not work because of rust borrowing rules and wired pointers
|
||||
// // lifetime annotation that I dont know how to use.
|
||||
// is_cancel: false,
|
||||
// in_progress: false,
|
||||
// // is_cancel: AtomicBool::new(false),
|
||||
// // in_progress: AtomicBool::new(false),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Animation {
|
||||
pub fn cancel(&mut self) {
|
||||
if !self.in_progress {
|
||||
return;
|
||||
}
|
||||
|
||||
self.is_cancel = true;
|
||||
let max_duration = Duration::from_secs(1);
|
||||
let spent_duration = Instant::now();
|
||||
|
||||
while self.in_progress {
|
||||
if spent_duration.elapsed() >= max_duration {
|
||||
self.in_progress = false;
|
||||
}
|
||||
|
||||
std::thread::sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn lerp(x: i32, new_x: i32, t: f64) -> i32 {
|
||||
let time = apply_ease_func(t);
|
||||
f64::from(new_x - x).mul_add(time, f64::from(x)) as i32
|
||||
}
|
||||
|
||||
pub fn lerp_rect(original_rect: &Rect, new_rect: &Rect, t: f64) -> Rect {
|
||||
Rect {
|
||||
left: Self::lerp(original_rect.left, new_rect.left, t),
|
||||
top: Self::lerp(original_rect.top, new_rect.top, t),
|
||||
right: Self::lerp(original_rect.right, new_rect.right, t),
|
||||
bottom: Self::lerp(original_rect.bottom, new_rect.bottom, t),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn animate(
|
||||
&mut self,
|
||||
duration: Duration,
|
||||
mut f: impl FnMut(f64) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.in_progress = true;
|
||||
// set target frame time to match 240 fps (my max refresh rate of monitor)
|
||||
// probably not the best way to do it is take actual monitor refresh rate
|
||||
// or make it configurable
|
||||
let target_frame_time = Duration::from_millis(1000 / 240);
|
||||
let mut progress = 0.0;
|
||||
let animation_start = Instant::now();
|
||||
|
||||
// start animation
|
||||
while progress < 1.0 {
|
||||
// check if animation is cancelled
|
||||
if self.is_cancel {
|
||||
// cancel animation
|
||||
// set all flags
|
||||
self.is_cancel = !self.is_cancel;
|
||||
self.in_progress = false;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tick_start = Instant::now();
|
||||
// calculate progress
|
||||
progress = animation_start.elapsed().as_millis() as f64 / duration.as_millis() as f64;
|
||||
f(progress).ok();
|
||||
|
||||
// sleep until next frame
|
||||
while tick_start.elapsed() < target_frame_time {
|
||||
std::thread::sleep(target_frame_time - tick_start.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
self.in_progress = false;
|
||||
|
||||
// limit progress to 1.0 if animation took longer
|
||||
if progress > 1.0 {
|
||||
progress = 1.0;
|
||||
}
|
||||
|
||||
// process animation for 1.0 to set target position
|
||||
f(progress)
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,15 @@ use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::Result;
|
||||
use windows::core::PCSTR;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
|
||||
use komorebi_core::Rect;
|
||||
|
||||
@@ -43,11 +43,11 @@ impl Border {
|
||||
}
|
||||
|
||||
pub fn create(name: &str) -> Result<()> {
|
||||
let name = format!("{name}\0");
|
||||
let name: Vec<u16> = format!("{name}\0").encode_utf16().collect();
|
||||
let instance = WindowsApi::module_handle_w()?;
|
||||
let class_name = PCSTR(name.as_ptr());
|
||||
let class_name = PCWSTR(name.as_ptr());
|
||||
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
|
||||
let window_class = WNDCLASSA {
|
||||
let window_class = WNDCLASSW {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: class_name,
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
@@ -56,18 +56,18 @@ impl Border {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _atom = WindowsApi::register_class_a(&window_class)?;
|
||||
let _atom = WindowsApi::register_class_w(&window_class)?;
|
||||
|
||||
let name_cl = name.clone();
|
||||
std::thread::spawn(move || -> Result<()> {
|
||||
let hwnd = WindowsApi::create_border_window(PCSTR(name_cl.as_ptr()), instance)?;
|
||||
let hwnd = WindowsApi::create_border_window(PCWSTR(name_cl.as_ptr()), instance)?;
|
||||
let border = Self::from(hwnd);
|
||||
|
||||
let mut message = MSG::default();
|
||||
|
||||
unsafe {
|
||||
while GetMessageA(&mut message, border.hwnd(), 0, 0).into() {
|
||||
DispatchMessageA(&message);
|
||||
while GetMessageW(&mut message, border.hwnd(), 0, 0).into() {
|
||||
DispatchMessageW(&message);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ impl Border {
|
||||
|
||||
let mut hwnd = HWND(0);
|
||||
while hwnd == HWND(0) {
|
||||
hwnd = unsafe { FindWindowA(PCSTR(name.as_ptr()), PCSTR::null()) };
|
||||
hwnd = unsafe { FindWindowW(PCWSTR(name.as_ptr()), PCWSTR::null()) };
|
||||
}
|
||||
|
||||
BORDER_HWND.store(hwnd.0, Ordering::SeqCst);
|
||||
|
||||
@@ -2,15 +2,15 @@ use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::Result;
|
||||
use windows::core::PCSTR;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::FindWindowW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
|
||||
use crate::windows_callbacks;
|
||||
use crate::WindowsApi;
|
||||
@@ -34,11 +34,11 @@ impl Hidden {
|
||||
}
|
||||
|
||||
pub fn create(name: &str) -> Result<()> {
|
||||
let name = format!("{name}\0");
|
||||
let name: Vec<u16> = format!("{name}\0").encode_utf16().collect();
|
||||
let instance = WindowsApi::module_handle_w()?;
|
||||
let class_name = PCSTR(name.as_ptr());
|
||||
let class_name = PCWSTR(name.as_ptr());
|
||||
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
|
||||
let window_class = WNDCLASSA {
|
||||
let window_class = WNDCLASSW {
|
||||
hInstance: instance.into(),
|
||||
lpszClassName: class_name,
|
||||
style: CS_HREDRAW | CS_VREDRAW,
|
||||
@@ -47,18 +47,18 @@ impl Hidden {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let _atom = WindowsApi::register_class_a(&window_class)?;
|
||||
let _atom = WindowsApi::register_class_w(&window_class)?;
|
||||
|
||||
let name_cl = name.clone();
|
||||
std::thread::spawn(move || -> Result<()> {
|
||||
let hwnd = WindowsApi::create_hidden_window(PCSTR(name_cl.as_ptr()), instance)?;
|
||||
let hwnd = WindowsApi::create_hidden_window(PCWSTR(name_cl.as_ptr()), instance)?;
|
||||
let hidden = Self::from(hwnd);
|
||||
|
||||
let mut message = MSG::default();
|
||||
|
||||
unsafe {
|
||||
while GetMessageA(&mut message, hidden.hwnd(), 0, 0).into() {
|
||||
DispatchMessageA(&message);
|
||||
while GetMessageW(&mut message, hidden.hwnd(), 0, 0).into() {
|
||||
DispatchMessageW(&message);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ impl Hidden {
|
||||
|
||||
let mut hwnd = HWND(0);
|
||||
while hwnd == HWND(0) {
|
||||
hwnd = unsafe { FindWindowA(PCSTR(name.as_ptr()), PCSTR::null()) };
|
||||
hwnd = unsafe { FindWindowW(PCWSTR(name.as_ptr()), PCWSTR::null()) };
|
||||
}
|
||||
|
||||
HIDDEN_HWND.store(hwnd.0, Ordering::SeqCst);
|
||||
|
||||
@@ -16,7 +16,6 @@ use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
@@ -27,7 +26,6 @@ use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::Backoff;
|
||||
use komorebi_core::EaseEnum;
|
||||
use lazy_static::lazy_static;
|
||||
use os_info::Version;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
@@ -37,8 +35,6 @@ use regex::Regex;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use sysinfo::Process;
|
||||
use sysinfo::ProcessExt;
|
||||
use sysinfo::SystemExt;
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
@@ -67,7 +63,6 @@ use crate::windows_api::WindowsApi;
|
||||
#[macro_use]
|
||||
mod ring;
|
||||
|
||||
mod animation;
|
||||
mod border;
|
||||
mod com;
|
||||
mod container;
|
||||
@@ -146,6 +141,8 @@ lazy_static! {
|
||||
]));
|
||||
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref DISPLAY_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, String>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, WorkspaceRule>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> =
|
||||
@@ -217,8 +214,6 @@ lazy_static! {
|
||||
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
|
||||
Arc::new(Mutex::new(None));
|
||||
|
||||
static ref ANIMATION_EASE: Arc<Mutex<EaseEnum>> = Arc::new(Mutex::new(EaseEnum::Linear));
|
||||
|
||||
// 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![]));
|
||||
@@ -242,8 +237,6 @@ pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
|
||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||
pub static ANIMATION_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub static ANIMATION_DURATION: AtomicU64 = AtomicU64::new(250);
|
||||
|
||||
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
|
||||
@@ -258,7 +251,7 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
|
||||
let appender = tracing_appender::rolling::never(&*DATA_DIR, "komorebi.log");
|
||||
let appender = tracing_appender::rolling::never(std::env::temp_dir(), "komorebi_plaintext.log");
|
||||
let color_appender = tracing_appender::rolling::never(std::env::temp_dir(), "komorebi.log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(appender);
|
||||
let (color_non_blocking, color_guard) = tracing_appender::non_blocking(color_appender);
|
||||
@@ -489,8 +482,10 @@ fn main() -> Result<()> {
|
||||
if matched_procs.len() > 1 {
|
||||
let mut len = matched_procs.len();
|
||||
for proc in matched_procs {
|
||||
if proc.root().ends_with("shims") {
|
||||
len -= 1;
|
||||
if let Some(root) = proc.root() {
|
||||
if root.ends_with("shims") {
|
||||
len -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ pub struct Monitor {
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
name: String,
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
device: Option<String>,
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
device_id: Option<String>,
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
size: Rect,
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
work_area_size: Rect,
|
||||
@@ -31,6 +35,9 @@ pub struct Monitor {
|
||||
work_area_offset: Option<Rect>,
|
||||
workspaces: Ring<Workspace>,
|
||||
#[serde(skip_serializing)]
|
||||
#[getset(get_copy = "pub", set = "pub")]
|
||||
last_focused_workspace: Option<usize>,
|
||||
#[serde(skip_serializing)]
|
||||
#[getset(get_mut = "pub")]
|
||||
workspace_names: HashMap<usize, String>,
|
||||
}
|
||||
@@ -44,10 +51,13 @@ pub fn new(id: isize, size: Rect, work_area_size: Rect, name: String) -> Monitor
|
||||
Monitor {
|
||||
id,
|
||||
name,
|
||||
device: None,
|
||||
device_id: None,
|
||||
size,
|
||||
work_area_size,
|
||||
work_area_offset: None,
|
||||
workspaces,
|
||||
last_focused_workspace: None,
|
||||
workspace_names: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::BufRead;
|
||||
@@ -47,9 +48,6 @@ use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::ALT_FOCUS_HACK;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_EASE;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
@@ -62,6 +60,7 @@ use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::CUSTOM_FFM;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
@@ -173,6 +172,23 @@ impl WindowManager {
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match message {
|
||||
SocketMessage::CycleFocusWorkspace(_) | SocketMessage::FocusWorkspaceNumber(_) => {
|
||||
if let Some(monitor) = self.focused_monitor_mut() {
|
||||
let idx = monitor.focused_workspace_idx();
|
||||
monitor.set_last_focused_workspace(Option::from(idx));
|
||||
}
|
||||
}
|
||||
SocketMessage::FocusMonitorWorkspaceNumber(target_monitor_idx, _) => {
|
||||
let idx = self.focused_workspace_idx_for_monitor_idx(target_monitor_idx)?;
|
||||
if let Some(monitor) = self.monitors_mut().get_mut(target_monitor_idx) {
|
||||
monitor.set_last_focused_workspace(Option::from(idx));
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match message {
|
||||
SocketMessage::Promote => self.promote_container_to_front()?,
|
||||
SocketMessage::PromoteFocus => self.promote_focus_to_front()?,
|
||||
@@ -599,6 +615,33 @@ impl WindowManager {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusLastWorkspace => {
|
||||
// This is to ensure that even on an empty workspace on a secondary monitor, the
|
||||
// secondary monitor where the cursor is focused will be used as the target for
|
||||
// the workspace switch op
|
||||
if let Some(monitor_idx) = self.monitor_idx_from_current_pos() {
|
||||
self.focus_monitor(monitor_idx)?;
|
||||
}
|
||||
|
||||
let idx = self
|
||||
.focused_monitor()
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?
|
||||
.focused_workspace_idx();
|
||||
|
||||
if let Some(monitor) = self.focused_monitor_mut() {
|
||||
if let Some(last_focused_workspace) = monitor.last_focused_workspace() {
|
||||
self.focus_workspace(last_focused_workspace)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.focused_monitor_mut()
|
||||
.ok_or_else(|| anyhow!("there is no monitor"))?
|
||||
.set_last_focused_workspace(Option::from(idx));
|
||||
|
||||
if BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
self.show_border()?;
|
||||
};
|
||||
}
|
||||
SocketMessage::FocusWorkspaceNumber(workspace_idx) => {
|
||||
// This is to ensure that even on an empty workspace on a secondary monitor, the
|
||||
// secondary monitor where the cursor is focused will be used as the target for
|
||||
@@ -676,6 +719,10 @@ impl WindowManager {
|
||||
},
|
||||
);
|
||||
}
|
||||
SocketMessage::DisplayIndexPreference(index_preference, ref display) => {
|
||||
let mut display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
|
||||
display_index_preferences.insert(index_preference, display.clone());
|
||||
}
|
||||
SocketMessage::EnsureWorkspaces(monitor_idx, workspace_count) => {
|
||||
self.ensure_workspaces_for_monitor(monitor_idx, workspace_count)?;
|
||||
}
|
||||
@@ -699,6 +746,32 @@ impl WindowManager {
|
||||
let mut stream = UnixStream::connect(socket)?;
|
||||
stream.write_all(state.as_bytes())?;
|
||||
}
|
||||
SocketMessage::VisibleWindows => {
|
||||
let mut monitor_visible_windows = HashMap::new();
|
||||
|
||||
for (index, monitor) in self.monitors().iter().enumerate() {
|
||||
if let Some(ws) = monitor.focused_workspace() {
|
||||
monitor_visible_windows.insert(
|
||||
monitor
|
||||
.device_id()
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("{index}")),
|
||||
ws.visible_window_details().clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let visible_windows_state =
|
||||
match serde_json::to_string_pretty(&monitor_visible_windows) {
|
||||
Ok(state) => state,
|
||||
Err(error) => error.to_string(),
|
||||
};
|
||||
|
||||
let socket = DATA_DIR.join("komorebic.sock");
|
||||
let mut stream = UnixStream::connect(socket)?;
|
||||
stream.write_all(visible_windows_state.as_bytes())?;
|
||||
}
|
||||
|
||||
SocketMessage::Query(query) => {
|
||||
let response = match query {
|
||||
StateQuery::FocusedMonitorIndex => self.focused_monitor_idx(),
|
||||
@@ -1143,15 +1216,6 @@ impl WindowManager {
|
||||
self.hide_border()?;
|
||||
}
|
||||
}
|
||||
SocketMessage::Animation(enable) => {
|
||||
ANIMATION_ENABLED.store(enable, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationDuration(duration) => {
|
||||
ANIMATION_DURATION.store(duration, Ordering::SeqCst);
|
||||
}
|
||||
SocketMessage::AnimationEase(ease) => {
|
||||
*ANIMATION_EASE.lock() = ease;
|
||||
}
|
||||
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
|
||||
match kind {
|
||||
WindowKind::Single => {
|
||||
@@ -1305,13 +1369,32 @@ impl WindowManager {
|
||||
| SocketMessage::InvisibleBorders(_)
|
||||
| SocketMessage::WorkAreaOffset(_)
|
||||
| SocketMessage::CycleMoveWindow(_)
|
||||
| SocketMessage::MoveWindow(_) => {
|
||||
| SocketMessage::MoveWindow(_)
|
||||
| SocketMessage::CycleFocusMonitor(_)
|
||||
| SocketMessage::CycleFocusWorkspace(_)
|
||||
| SocketMessage::FocusMonitorNumber(_)
|
||||
| SocketMessage::FocusMonitorWorkspaceNumber(_, _)
|
||||
| SocketMessage::FocusWorkspaceNumber(_) => {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window::new(foreground);
|
||||
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() {
|
||||
BORDER_COLOUR_CURRENT.store(
|
||||
monocle,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
}
|
||||
|
||||
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
|
||||
if stack != 0 && self.focused_container()?.windows().len() > 1 {
|
||||
BORDER_COLOUR_CURRENT
|
||||
.store(stack, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, &self.invisible_borders, false)?;
|
||||
}
|
||||
|
||||
@@ -515,8 +515,7 @@ impl WindowManager {
|
||||
WindowManagerEvent::DisplayChange(..)
|
||||
| WindowManagerEvent::MouseCapture(..)
|
||||
| WindowManagerEvent::Cloak(..)
|
||||
| WindowManagerEvent::Uncloak(..)
|
||||
| WindowManagerEvent::UpdateFocusedWindowBorder(..) => {}
|
||||
| WindowManagerEvent::Uncloak(..) => {}
|
||||
};
|
||||
|
||||
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
|
||||
@@ -530,8 +529,7 @@ impl WindowManager {
|
||||
| WindowManagerEvent::Show(_, window)
|
||||
| WindowManagerEvent::FocusChange(_, window)
|
||||
| WindowManagerEvent::Hide(_, window)
|
||||
| WindowManagerEvent::Minimize(_, window)
|
||||
| WindowManagerEvent::UpdateFocusedWindowBorder(window) => {
|
||||
| WindowManagerEvent::Minimize(_, window) => {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
let mut target_window = None;
|
||||
let mut target_window_is_monocle = false;
|
||||
@@ -597,10 +595,6 @@ impl WindowManager {
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
border.set_position(target_window, &self.invisible_borders, activate)?;
|
||||
|
||||
if matches!(event, WindowManagerEvent::UpdateFocusedWindowBorder(_)) {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
}
|
||||
|
||||
if activate {
|
||||
BORDER_HIDDEN.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -7,9 +7,6 @@ use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::ALT_FOCUS_HACK;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_EASE;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_MONOCLE;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
@@ -22,6 +19,7 @@ use crate::BORDER_WIDTH;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
@@ -42,7 +40,6 @@ use komorebi_core::config_generation::MatchingStrategy;
|
||||
use komorebi_core::resolve_home_path;
|
||||
use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::DefaultLayout;
|
||||
use komorebi_core::EaseEnum;
|
||||
use komorebi_core::FocusFollowsMouseImplementation;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Layout;
|
||||
@@ -316,15 +313,9 @@ pub struct StaticConfig {
|
||||
/// Set monitor index preferences
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub monitor_index_preferences: Option<HashMap<usize, Rect>>,
|
||||
/// Enable or disable animations (default: false)
|
||||
/// Set display index preferences
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation: Option<bool>,
|
||||
/// Set the animation ease function (default: Linear)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation_ease: Option<EaseEnum>,
|
||||
/// Set the animation duration in ms (default: 250)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub animation_duration: Option<u64>,
|
||||
pub display_index_preferences: Option<HashMap<usize, String>>,
|
||||
}
|
||||
|
||||
impl From<&WindowManager> for StaticConfig {
|
||||
@@ -445,9 +436,7 @@ impl From<&WindowManager> for StaticConfig {
|
||||
layered_applications: None,
|
||||
object_name_change_applications: None,
|
||||
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
|
||||
animation: Option::from(ANIMATION_ENABLED.load(Ordering::SeqCst)),
|
||||
animation_duration: Option::from(ANIMATION_DURATION.load(Ordering::SeqCst)),
|
||||
animation_ease: Option::from(*ANIMATION_EASE.lock()),
|
||||
display_index_preferences: Option::from(DISPLAY_INDEX_PREFERENCES.lock().clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -460,24 +449,16 @@ impl StaticConfig {
|
||||
*preferences = monitor_index_preferences.clone();
|
||||
}
|
||||
|
||||
if let Some(display_index_preferences) = &self.display_index_preferences {
|
||||
let mut preferences = DISPLAY_INDEX_PREFERENCES.lock();
|
||||
*preferences = display_index_preferences.clone();
|
||||
}
|
||||
|
||||
if let Some(behaviour) = self.window_hiding_behaviour {
|
||||
let mut window_hiding_behaviour = HIDING_BEHAVIOUR.lock();
|
||||
*window_hiding_behaviour = behaviour;
|
||||
}
|
||||
|
||||
if let Some(animation) = self.animation {
|
||||
ANIMATION_ENABLED.store(animation, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(duration) = self.animation_duration {
|
||||
ANIMATION_DURATION.store(duration, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(ease) = self.animation_ease {
|
||||
let mut animation_ease = ANIMATION_EASE.lock();
|
||||
*animation_ease = ease;
|
||||
}
|
||||
|
||||
if let Some(hack) = self.alt_focus_hack {
|
||||
ALT_FOCUS_HACK.store(hack, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use crate::com::SetCloak;
|
||||
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
|
||||
use crate::ANIMATION_DURATION;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::fmt::Write as _;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
||||
use color_eyre::eyre;
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use komorebi_core::config_generation::IdWithIdentifier;
|
||||
@@ -29,7 +26,6 @@ use komorebi_core::ApplicationIdentifier;
|
||||
use komorebi_core::HidingBehaviour;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::animation::Animation;
|
||||
use crate::styles::ExtendedWindowStyle;
|
||||
use crate::styles::WindowStyle;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
@@ -49,7 +45,26 @@ use crate::WSL2_UI_PROCESSES;
|
||||
#[derive(Debug, Clone, Copy, JsonSchema)]
|
||||
pub struct Window {
|
||||
pub(crate) hwnd: isize,
|
||||
animation: Animation,
|
||||
}
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
#[derive(Debug, Clone, Serialize, JsonSchema)]
|
||||
pub struct WindowDetails {
|
||||
pub title: String,
|
||||
pub exe: String,
|
||||
pub class: String,
|
||||
}
|
||||
|
||||
impl TryFrom<Window> for WindowDetails {
|
||||
type Error = eyre::ErrReport;
|
||||
|
||||
fn try_from(value: Window) -> std::result::Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
title: value.title()?,
|
||||
exe: value.exe()?,
|
||||
class: value.class()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Window {
|
||||
@@ -109,14 +124,6 @@ impl Serialize for Window {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
// for instantiation of animation struct
|
||||
pub fn new(hwnd: isize) -> Self {
|
||||
Self {
|
||||
hwnd,
|
||||
animation: Animation::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn hwnd(self) -> HWND {
|
||||
HWND(self.hwnd)
|
||||
}
|
||||
@@ -136,47 +143,6 @@ impl Window {
|
||||
true,
|
||||
)
|
||||
}
|
||||
pub fn animate_position(&mut self, layout: &Rect, top: bool) -> Result<()> {
|
||||
let hwnd = self.hwnd();
|
||||
let curr_rect = WindowsApi::window_rect(hwnd).unwrap();
|
||||
|
||||
if curr_rect.left == layout.left
|
||||
&& curr_rect.top == layout.top
|
||||
&& curr_rect.bottom == layout.bottom
|
||||
&& curr_rect.right == layout.right
|
||||
{
|
||||
WindowsApi::position_window(hwnd, layout, top)
|
||||
} else {
|
||||
let target_rect = *layout;
|
||||
let duration = Duration::from_millis(ANIMATION_DURATION.load(Ordering::SeqCst));
|
||||
let mut animation = self.animation;
|
||||
|
||||
let self_copied = *self;
|
||||
std::thread::spawn(move || {
|
||||
animation.animate(duration, |progress: f64| {
|
||||
let new_rect = Animation::lerp_rect(&curr_rect, &target_rect, progress);
|
||||
if progress < 1.0 {
|
||||
// using MoveWindow because it runs faster than SetWindowPos
|
||||
// so animation have more fps and feel smoother
|
||||
WindowsApi::move_window(hwnd, &new_rect, true)?;
|
||||
} else {
|
||||
WindowsApi::position_window(hwnd, &new_rect, top)?;
|
||||
|
||||
if WindowsApi::foreground_window()? == self_copied.hwnd {
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
.send(WindowManagerEvent::UpdateFocusedWindowBorder(self_copied))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_position(
|
||||
&mut self,
|
||||
@@ -209,17 +175,7 @@ impl Window {
|
||||
rect.bottom += invisible_borders.bottom;
|
||||
}
|
||||
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
// check if animation is in progress
|
||||
if self.animation.in_progress {
|
||||
// wait for cancel animation
|
||||
self.animation.cancel();
|
||||
}
|
||||
|
||||
self.animate_position(&rect, top)
|
||||
} else {
|
||||
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||
}
|
||||
WindowsApi::position_window(self.hwnd(), &rect, top)
|
||||
}
|
||||
|
||||
pub fn hide(self) {
|
||||
|
||||
@@ -49,6 +49,7 @@ use crate::workspace::Workspace;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HOME_DIR;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
@@ -103,6 +104,7 @@ pub struct State {
|
||||
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>,
|
||||
}
|
||||
|
||||
impl AsRef<Self> for WindowManager {
|
||||
@@ -132,6 +134,7 @@ impl From<&WindowManager> for State {
|
||||
border_overflow_identifiers: BORDER_OVERFLOW_IDENTIFIERS.lock().clone(),
|
||||
name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(),
|
||||
monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(),
|
||||
display_index_preferences: DISPLAY_INDEX_PREFERENCES.lock().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +220,7 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn show_border(&self) -> Result<()> {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window::new(foreground);
|
||||
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;
|
||||
@@ -589,7 +592,7 @@ impl WindowManager {
|
||||
|
||||
// Hide the window we are about to remove if it is on the currently focused workspace
|
||||
if op.is_origin(focused_monitor_idx, focused_workspace_idx) {
|
||||
Window::new(op.hwnd).hide();
|
||||
Window { hwnd: op.hwnd }.hide();
|
||||
should_update_focused_workspace = true;
|
||||
}
|
||||
|
||||
@@ -619,7 +622,7 @@ impl WindowManager {
|
||||
.get_mut(op.target_workspace_idx)
|
||||
.ok_or_else(|| anyhow!("there is no workspace with that index"))?;
|
||||
|
||||
target_workspace.new_container_for_window(Window::new(op.hwnd));
|
||||
target_workspace.new_container_for_window(Window { hwnd: op.hwnd });
|
||||
}
|
||||
|
||||
// Only re-tile the focused workspace if we need to
|
||||
@@ -663,14 +666,14 @@ impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn manage_focused_window(&mut self) -> Result<()> {
|
||||
let hwnd = WindowsApi::foreground_window()?;
|
||||
let event = WindowManagerEvent::Manage(Window::new(hwnd));
|
||||
let event = WindowManagerEvent::Manage(Window { hwnd });
|
||||
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::new(hwnd));
|
||||
let event = WindowManagerEvent::Unmanage(Window { hwnd });
|
||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||
}
|
||||
|
||||
@@ -708,14 +711,13 @@ impl WindowManager {
|
||||
];
|
||||
|
||||
if !known_hwnd {
|
||||
let class = Window::new(hwnd).class()?;
|
||||
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::new(hwnd).exe()?)
|
||||
if let Some(exe_hwnd) = workspace.hwnd_from_exe(&Window { hwnd }.exe()?)
|
||||
{
|
||||
hwnd = exe_hwnd;
|
||||
known_hwnd = true;
|
||||
@@ -726,11 +728,11 @@ impl WindowManager {
|
||||
}
|
||||
|
||||
if known_hwnd {
|
||||
let event = WindowManagerEvent::Raise(Window::new(hwnd));
|
||||
let event = WindowManagerEvent::Raise(Window { hwnd });
|
||||
self.has_pending_raise_op = true;
|
||||
Ok(WINEVENT_CALLBACK_CHANNEL.lock().0.send(event)?)
|
||||
} else {
|
||||
tracing::debug!("not raising unknown window: {}", Window::new(hwnd,));
|
||||
tracing::debug!("not raising unknown window: {}", Window { hwnd });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -844,7 +846,9 @@ impl WindowManager {
|
||||
} else if let Ok(window) = self.focused_window_mut() {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
} else {
|
||||
let desktop_window = Window::new(WindowsApi::desktop_window()?);
|
||||
let desktop_window = Window {
|
||||
hwnd: WindowsApi::desktop_window()?,
|
||||
};
|
||||
|
||||
let rect = self.focused_monitor_size()?;
|
||||
WindowsApi::center_cursor_in_rect(&rect)?;
|
||||
@@ -1318,7 +1322,7 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.id();
|
||||
|
||||
if !WindowsApi::monitors_have_same_scale_factor(a, b)? {
|
||||
if !WindowsApi::monitors_have_same_dpi(a, b)? {
|
||||
self.update_focused_workspace(self.mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
@@ -2194,6 +2198,14 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("there is no workspace"))
|
||||
}
|
||||
|
||||
pub fn focused_workspace_idx_for_monitor_idx(&self, idx: usize) -> Result<usize> {
|
||||
Ok(self
|
||||
.monitors()
|
||||
.get(idx)
|
||||
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
|
||||
.focused_workspace_idx())
|
||||
}
|
||||
|
||||
pub fn focused_workspace_for_monitor_idx(&self, idx: usize) -> Result<&Workspace> {
|
||||
self.monitors()
|
||||
.get(idx)
|
||||
|
||||
@@ -27,7 +27,6 @@ pub enum WindowManagerEvent {
|
||||
Unmanage(Window),
|
||||
Raise(Window),
|
||||
DisplayChange(Window),
|
||||
UpdateFocusedWindowBorder(Window),
|
||||
}
|
||||
|
||||
impl Display for WindowManagerEvent {
|
||||
@@ -78,9 +77,6 @@ impl Display for WindowManagerEvent {
|
||||
Self::DisplayChange(window) => {
|
||||
write!(f, "DisplayChange (Window: {window})")
|
||||
}
|
||||
Self::UpdateFocusedWindowBorder(window) => {
|
||||
write!(f, "UpdateFocusedBorderWindow (Window: {window})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,8 +97,7 @@ impl WindowManagerEvent {
|
||||
| Self::Raise(window)
|
||||
| Self::Manage(window)
|
||||
| Self::DisplayChange(window)
|
||||
| Self::Unmanage(window)
|
||||
| Self::UpdateFocusedWindowBorder(window) => window,
|
||||
| Self::Unmanage(window) => window,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::c_void;
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::Error;
|
||||
use color_eyre::Result;
|
||||
use widestring::U16CStr;
|
||||
use windows::core::Result as WindowsCrateResult;
|
||||
use windows::core::PCSTR;
|
||||
use windows::core::PCWSTR;
|
||||
use windows::core::PWSTR;
|
||||
use windows::Win32::Foundation::CloseHandle;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
@@ -30,11 +29,13 @@ use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
|
||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
|
||||
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||
use windows::Win32::Graphics::Gdi::EnumDisplayDevicesW;
|
||||
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
|
||||
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::DISPLAY_DEVICEW;
|
||||
use windows::Win32::Graphics::Gdi::HBRUSH;
|
||||
use windows::Win32::Graphics::Gdi::HDC;
|
||||
use windows::Win32::Graphics::Gdi::HMONITOR;
|
||||
@@ -51,8 +52,10 @@ use windows::Win32::System::Threading::QueryFullProcessImageNameW;
|
||||
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::SetProcessDpiAwarenessContext;
|
||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||
use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyState;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
|
||||
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
||||
@@ -63,11 +66,9 @@ 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_MENU;
|
||||
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
|
||||
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
|
||||
@@ -81,10 +82,9 @@ 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::MoveWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
||||
@@ -94,6 +94,7 @@ use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EDD_GET_DEVICE_INTERFACE_NAME;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
|
||||
@@ -118,7 +119,7 @@ use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||
@@ -165,8 +166,8 @@ impl_from_integer_for_windows_result!(usize, isize, u16, u32, i32);
|
||||
impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
|
||||
fn from(result: WindowsResult<T, E>) -> Self {
|
||||
match result {
|
||||
WindowsResult::Err(error) => Self::Err(error),
|
||||
WindowsResult::Ok(ok) => Self::Ok(ok),
|
||||
WindowsResult::Err(error) => Err(error),
|
||||
WindowsResult::Ok(ok) => Ok(ok),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +223,7 @@ impl WindowsApi {
|
||||
let mut monitors: Vec<(String, isize)> = vec![];
|
||||
let monitors_ref: &mut Vec<(String, isize)> = monitors.as_mut();
|
||||
Self::enum_display_monitors(
|
||||
Option::Some(windows_callbacks::valid_display_monitors),
|
||||
Some(windows_callbacks::valid_display_monitors),
|
||||
monitors_ref as *mut Vec<(String, isize)> as isize,
|
||||
)?;
|
||||
|
||||
@@ -231,9 +232,46 @@ impl WindowsApi {
|
||||
|
||||
pub fn load_monitor_information(monitors: &mut Ring<Monitor>) -> Result<()> {
|
||||
Self::enum_display_monitors(
|
||||
Option::Some(windows_callbacks::enum_display_monitor),
|
||||
Some(windows_callbacks::enum_display_monitor),
|
||||
monitors as *mut Ring<Monitor> as isize,
|
||||
)
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn enum_display_devices(
|
||||
index: u32,
|
||||
lp_device: Option<*const u16>,
|
||||
) -> Result<DISPLAY_DEVICEW> {
|
||||
#[allow(clippy::option_if_let_else)]
|
||||
let lp_device = match lp_device {
|
||||
None => PCWSTR::null(),
|
||||
Some(lp_device) => PCWSTR(lp_device),
|
||||
};
|
||||
|
||||
let mut display_device = DISPLAY_DEVICEW {
|
||||
cb: u32::try_from(std::mem::size_of::<DISPLAY_DEVICEW>())?,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match unsafe {
|
||||
EnumDisplayDevicesW(
|
||||
lp_device,
|
||||
index,
|
||||
std::ptr::addr_of_mut!(display_device),
|
||||
EDD_GET_DEVICE_INTERFACE_NAME,
|
||||
)
|
||||
}
|
||||
.ok()
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
tracing::error!("enum_display_devices: {}", error);
|
||||
return Err(error.into());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(display_device)
|
||||
}
|
||||
|
||||
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> Result<()> {
|
||||
@@ -246,7 +284,7 @@ impl WindowsApi {
|
||||
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
|
||||
// EnumWindows will enumerate through windows on all monitors
|
||||
Self::enum_windows(
|
||||
Option::Some(windows_callbacks::enum_window),
|
||||
Some(windows_callbacks::enum_window),
|
||||
workspace.containers_mut() as *mut VecDeque<Container> as isize,
|
||||
)?;
|
||||
|
||||
@@ -355,20 +393,6 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn move_window(hwnd: HWND, layout: &Rect, repaint: bool) -> Result<()> {
|
||||
unsafe {
|
||||
MoveWindow(
|
||||
hwnd,
|
||||
layout.left,
|
||||
layout.top,
|
||||
layout.right,
|
||||
layout.bottom,
|
||||
repaint,
|
||||
)
|
||||
}
|
||||
.process()
|
||||
}
|
||||
|
||||
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
|
||||
@@ -665,10 +689,10 @@ impl WindowsApi {
|
||||
|
||||
pub fn monitor(hmonitor: isize) -> Result<Monitor> {
|
||||
let ex_info = Self::monitor_info_w(HMONITOR(hmonitor))?;
|
||||
let name = OsString::from_wide(&ex_info.szDevice);
|
||||
let name = name
|
||||
let name = U16CStr::from_slice_truncate(&ex_info.szDevice)
|
||||
.expect("monitor name was not a valid u16 c string")
|
||||
.to_ustring()
|
||||
.to_string_lossy()
|
||||
.replace('\u{0000}', "")
|
||||
.trim_start_matches(r"\\.\")
|
||||
.to_string();
|
||||
|
||||
@@ -774,19 +798,33 @@ impl WindowsApi {
|
||||
unsafe { CreateSolidBrush(COLORREF(colour)) }
|
||||
}
|
||||
|
||||
pub fn register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
|
||||
Result::from(WindowsResult::from(unsafe { RegisterClassA(window_class) }))
|
||||
pub fn register_class_w(window_class: &WNDCLASSW) -> Result<u16> {
|
||||
Result::from(WindowsResult::from(unsafe { RegisterClassW(window_class) }))
|
||||
}
|
||||
|
||||
pub fn scale_factor_for_monitor(hmonitor: isize) -> Result<DEVICE_SCALE_FACTOR> {
|
||||
unsafe { GetScaleFactorForMonitor(HMONITOR(hmonitor)) }.process()
|
||||
pub fn dpi_for_monitor(hmonitor: isize) -> Result<f32> {
|
||||
let mut dpi_x = u32::default();
|
||||
let mut dpi_y = u32::default();
|
||||
|
||||
unsafe {
|
||||
GetDpiForMonitor(
|
||||
HMONITOR(hmonitor),
|
||||
MDT_EFFECTIVE_DPI,
|
||||
std::ptr::addr_of_mut!(dpi_x),
|
||||
std::ptr::addr_of_mut!(dpi_y),
|
||||
)
|
||||
}
|
||||
.process()?;
|
||||
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
Ok(dpi_y as f32 / 96.0)
|
||||
}
|
||||
|
||||
pub fn monitors_have_same_scale_factor(a: isize, b: isize) -> Result<bool> {
|
||||
let a = Self::scale_factor_for_monitor(a)?;
|
||||
let b = Self::scale_factor_for_monitor(b)?;
|
||||
pub fn monitors_have_same_dpi(hmonitor_a: isize, hmonitor_b: isize) -> Result<bool> {
|
||||
let dpi_a = Self::dpi_for_monitor(hmonitor_a)?;
|
||||
let dpi_b = Self::dpi_for_monitor(hmonitor_b)?;
|
||||
|
||||
Ok(a == b)
|
||||
Ok((dpi_a - dpi_b).abs() < f32::EPSILON)
|
||||
}
|
||||
|
||||
pub fn round_corners(hwnd: isize) -> Result<()> {
|
||||
@@ -803,9 +841,9 @@ impl WindowsApi {
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn create_border_window(name: PCSTR, instance: HMODULE) -> Result<isize> {
|
||||
pub fn create_border_window(name: PCWSTR, instance: HMODULE) -> Result<isize> {
|
||||
unsafe {
|
||||
let hwnd = CreateWindowExA(
|
||||
let hwnd = CreateWindowExW(
|
||||
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
|
||||
name,
|
||||
name,
|
||||
@@ -837,9 +875,9 @@ impl WindowsApi {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_hidden_window(name: PCSTR, instance: HMODULE) -> Result<isize> {
|
||||
pub fn create_hidden_window(name: PCWSTR, instance: HMODULE) -> Result<isize> {
|
||||
unsafe {
|
||||
CreateWindowExA(
|
||||
CreateWindowExW(
|
||||
WS_EX_NOACTIVATE,
|
||||
name,
|
||||
name,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
use widestring::U16CStr;
|
||||
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::COLORREF;
|
||||
@@ -40,6 +41,7 @@ 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;
|
||||
|
||||
@@ -72,7 +74,38 @@ pub extern "system" fn enum_display_monitor(
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(m) = WindowsApi::monitor(hmonitor.0) {
|
||||
let current_index = monitors.elements().len();
|
||||
|
||||
if let Ok(mut m) = WindowsApi::monitor(hmonitor.0) {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
if let Ok(d) = WindowsApi::enum_display_devices(current_index as u32, None) {
|
||||
let name = U16CStr::from_slice_truncate(d.DeviceName.as_ref())
|
||||
.expect("display device name was not a valid u16 c string")
|
||||
.to_ustring()
|
||||
.to_string_lossy()
|
||||
.trim_start_matches(r"\\.\")
|
||||
.to_string();
|
||||
|
||||
if name.eq(m.name()) {
|
||||
if let Ok(device) = WindowsApi::enum_display_devices(0, Some(d.DeviceName.as_ptr()))
|
||||
{
|
||||
let id = U16CStr::from_slice_truncate(device.DeviceID.as_ref())
|
||||
.expect("display device id was not a valid u16 c string")
|
||||
.to_ustring()
|
||||
.to_string_lossy()
|
||||
.trim_start_matches(r"\\?\")
|
||||
.to_string();
|
||||
|
||||
let mut split: Vec<_> = id.split('#').collect();
|
||||
split.remove(0);
|
||||
split.remove(split.len() - 1);
|
||||
|
||||
m.set_device(Option::from(split[0].to_string()));
|
||||
m.set_device_id(Option::from(split.join("-")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
|
||||
let mut index_preference = None;
|
||||
for (index, monitor_size) in &*monitor_index_preferences {
|
||||
@@ -81,7 +114,18 @@ pub extern "system" fn enum_display_monitor(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(preference) = index_preference {
|
||||
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.lock();
|
||||
for (index, device) in &*display_index_preferences {
|
||||
if let Some(known_device) = m.device_id() {
|
||||
if device == known_device {
|
||||
index_preference = Option::from(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if monitors.elements().is_empty() {
|
||||
monitors.elements_mut().push_back(m);
|
||||
} else if let Some(preference) = index_preference {
|
||||
let current_len = monitors.elements().len();
|
||||
if *preference > current_len {
|
||||
monitors.elements_mut().reserve(1);
|
||||
@@ -104,7 +148,7 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
let is_minimized = WindowsApi::is_iconic(hwnd);
|
||||
|
||||
if is_visible && is_window && !is_minimized {
|
||||
let window = Window::new(hwnd.0);
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
|
||||
if let Ok(should_manage) = window.should_manage(None) {
|
||||
if should_manage {
|
||||
@@ -132,7 +176,7 @@ pub extern "system" fn win_event_hook(
|
||||
return;
|
||||
}
|
||||
|
||||
let window = Window::new(hwnd.0);
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
|
||||
let winevent = unsafe { ::std::mem::transmute(event) };
|
||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||
@@ -196,7 +240,7 @@ pub extern "system" fn hidden_window(
|
||||
unsafe {
|
||||
match message {
|
||||
WM_DISPLAYCHANGE => {
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
@@ -211,7 +255,7 @@ pub extern "system" fn hidden_window(
|
||||
if wparam.0 as u32 == SPI_SETWORKAREA.0
|
||||
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
|
||||
{
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
@@ -224,7 +268,7 @@ pub extern "system" fn hidden_window(
|
||||
WM_DEVICECHANGE => {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window::new(window.0));
|
||||
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
|
||||
WINEVENT_CALLBACK_CHANNEL
|
||||
.lock()
|
||||
.0
|
||||
|
||||
@@ -19,15 +19,12 @@ use komorebi_core::Layout;
|
||||
use komorebi_core::OperationDirection;
|
||||
use komorebi_core::Rect;
|
||||
|
||||
use crate::border::Border;
|
||||
use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::static_config::WorkspaceConfig;
|
||||
use crate::window::Window;
|
||||
use crate::window::WindowDetails;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::ANIMATION_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
@@ -248,12 +245,6 @@ impl Workspace {
|
||||
}
|
||||
|
||||
if *self.tile() {
|
||||
if ANIMATION_ENABLED.load(Ordering::SeqCst) {
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.hide()?;
|
||||
BORDER_HIDDEN.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
if let Some(container) = self.monocle_container_mut() {
|
||||
if let Some(window) = container.focused_window_mut() {
|
||||
adjusted_work_area.add_padding(container_padding);
|
||||
@@ -1097,6 +1088,20 @@ impl Workspace {
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn visible_window_details(&self) -> Vec<WindowDetails> {
|
||||
let mut vec: Vec<WindowDetails> = vec![];
|
||||
|
||||
for container in self.containers() {
|
||||
if let Some(focused) = container.focused_window() {
|
||||
if let Ok(details) = (*focused).try_into() {
|
||||
vec.push(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn visible_windows_mut(&mut self) -> Vec<Option<&mut Window>> {
|
||||
let mut vec = vec![];
|
||||
for container in self.containers_mut() {
|
||||
|
||||
@@ -15,19 +15,21 @@ derive-ahk = { path = "../derive-ahk" }
|
||||
komorebi-core = { path = "../komorebi-core" }
|
||||
|
||||
clap = { version = "4", features = ["derive", "wrap_help"] }
|
||||
color-eyre = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
fs-tail = "0.1"
|
||||
heck = "0.4"
|
||||
lazy_static = "1"
|
||||
miette = { version = "5", features = ["fancy"] }
|
||||
paste = "1"
|
||||
powershell_script = "1.0"
|
||||
reqwest = { version = "0.11", features = ["blocking"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9"
|
||||
sysinfo = "0.29"
|
||||
sysinfo = "0.30"
|
||||
thiserror = "1"
|
||||
uds_windows = "1"
|
||||
which = "5"
|
||||
windows = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
@@ -7,6 +7,7 @@ use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
@@ -21,10 +22,12 @@ use color_eyre::Result;
|
||||
use fs_tail::TailedFile;
|
||||
use heck::ToKebabCase;
|
||||
use komorebi_core::resolve_home_path;
|
||||
use komorebi_core::EaseEnum;
|
||||
use lazy_static::lazy_static;
|
||||
use miette::NamedSource;
|
||||
use miette::Report;
|
||||
use miette::SourceOffset;
|
||||
use miette::SourceSpan;
|
||||
use paste::paste;
|
||||
use sysinfo::SystemExt;
|
||||
use uds_windows::UnixListener;
|
||||
use uds_windows::UnixStream;
|
||||
use which::which;
|
||||
@@ -83,6 +86,17 @@ trait AhkFunction {
|
||||
fn generate_ahk_function() -> String;
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, miette::Diagnostic)]
|
||||
#[error("{message}")]
|
||||
#[diagnostic(code(komorebi::configuration), help("try fixing this syntax error"))]
|
||||
struct ConfigurationError {
|
||||
message: String,
|
||||
#[source_code]
|
||||
src: NamedSource,
|
||||
#[label("This bit here")]
|
||||
bad_bit: SourceSpan,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, ValueEnum)]
|
||||
enum BooleanState {
|
||||
Enable,
|
||||
@@ -402,6 +416,14 @@ struct MonitorIndexPreference {
|
||||
bottom: i32,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct DisplayIndexPreference {
|
||||
/// Preferred monitor index (zero-indexed)
|
||||
index_preference: usize,
|
||||
/// Display name as identified in komorebic state
|
||||
display: String,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct EnsureWorkspaces {
|
||||
/// Monitor index (zero-indexed)
|
||||
@@ -628,25 +650,6 @@ struct ActiveWindowBorderOffset {
|
||||
offset: i32,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct Animation {
|
||||
#[clap(value_enum)]
|
||||
boolean_state: BooleanState,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct AnimationDuration {
|
||||
/// Desired animation durations in ms
|
||||
duration: u64,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct AnimationEase {
|
||||
/// Desired ease function for animation
|
||||
#[clap(value_enum, short, long, default_value = "linear")]
|
||||
ease_func: EaseEnum,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
struct Start {
|
||||
@@ -770,6 +773,8 @@ enum SubCommand {
|
||||
Check,
|
||||
/// Show a JSON representation of the current window manager state
|
||||
State,
|
||||
/// Show a JSON representation of visible windows
|
||||
VisibleWindows,
|
||||
/// Query the current window manager state
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Query(Query),
|
||||
@@ -864,6 +869,8 @@ enum SubCommand {
|
||||
/// Focus the specified monitor
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusMonitor(FocusMonitor),
|
||||
/// Focus the last focused workspace on the focused monitor
|
||||
FocusLastWorkspace,
|
||||
/// Focus the specified workspace on the focused monitor
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusWorkspace(FocusWorkspace),
|
||||
@@ -935,6 +942,9 @@ enum SubCommand {
|
||||
/// Set the monitor index preference for a monitor identified using its size
|
||||
#[clap(arg_required_else_help = true)]
|
||||
MonitorIndexPreference(MonitorIndexPreference),
|
||||
/// Set the display index preference for a monitor identified using its display name
|
||||
#[clap(arg_required_else_help = true)]
|
||||
DisplayIndexPreference(DisplayIndexPreference),
|
||||
/// Create at least this many workspaces for the specified monitor
|
||||
#[clap(arg_required_else_help = true)]
|
||||
EnsureWorkspaces(EnsureWorkspaces),
|
||||
@@ -1079,15 +1089,6 @@ enum SubCommand {
|
||||
/// Set the offset for the active window border
|
||||
#[clap(arg_required_else_help = true)]
|
||||
ActiveWindowBorderOffset(ActiveWindowBorderOffset),
|
||||
/// Enable or disable the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Animation(Animation),
|
||||
/// Set the duration for the window move animation in ms
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationDuration(AnimationDuration),
|
||||
/// Set the ease function for the window move animation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
AnimationEase(AnimationEase),
|
||||
/// Enable or disable focus follows mouse for the operating system
|
||||
#[clap(arg_required_else_help = true)]
|
||||
FocusFollowsMouse(FocusFollowsMouse),
|
||||
@@ -1236,7 +1237,7 @@ fn main() -> Result<()> {
|
||||
let mut arguments = String::from("start");
|
||||
|
||||
if let Some(config) = args.config {
|
||||
arguments.push_str("--config ");
|
||||
arguments.push_str(" --config ");
|
||||
arguments.push_str(&config.to_string_lossy());
|
||||
}
|
||||
|
||||
@@ -1290,7 +1291,49 @@ fn main() -> Result<()> {
|
||||
.join("whkdrc");
|
||||
|
||||
if static_config.exists() {
|
||||
let config_source = std::fs::read_to_string(&static_config)?;
|
||||
let lines: Vec<_> = config_source.lines().collect();
|
||||
let parsed_config = serde_json::from_str::<serde_json::Value>(&config_source);
|
||||
if let Err(serde_error) = &parsed_config {
|
||||
let line = lines[serde_error.line() - 2];
|
||||
|
||||
let offset = SourceOffset::from_location(
|
||||
config_source.clone(),
|
||||
serde_error.line() - 1,
|
||||
line.len(),
|
||||
);
|
||||
|
||||
let error_string = serde_error.to_string();
|
||||
let msgs: Vec<_> = error_string.split(" at ").collect();
|
||||
|
||||
let diagnostic = ConfigurationError {
|
||||
message: msgs[0].to_string(),
|
||||
src: NamedSource::new("komorebi.json", config_source.clone()),
|
||||
bad_bit: SourceSpan::new(offset, 2.into()),
|
||||
};
|
||||
|
||||
println!("{:?}", Report::new(diagnostic));
|
||||
}
|
||||
|
||||
println!("Found komorebi.json; this file can be passed to the start command with the --config flag\n");
|
||||
|
||||
if let Ok(config) = &parsed_config {
|
||||
if let Some(asc_path) = config.get("app_specific_configuration_path") {
|
||||
let normalized_asc_path = asc_path
|
||||
.to_string()
|
||||
.replace(
|
||||
"$Env:USERPROFILE",
|
||||
&dirs::home_dir().unwrap().to_string_lossy(),
|
||||
)
|
||||
.replace('"', "")
|
||||
.replace('\\', "/");
|
||||
|
||||
if !Path::exists(Path::new(&normalized_asc_path)) {
|
||||
println!("Application specific configuration file path '{normalized_asc_path}' does not exist. Try running 'komorebic fetch-asc'\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if config_whkd.exists() {
|
||||
println!("Found ~/.config/whkdrc; key bindings will be loaded from here when whkd is started, and you can start it automatically using the --whkd flag\n");
|
||||
} else {
|
||||
@@ -1742,7 +1785,7 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
Start-Process {ahk} {config} -WindowStyle hidden
|
||||
Start-Process '{ahk}' '{config}' -WindowStyle hidden
|
||||
"#,
|
||||
config = config_ahk.display()
|
||||
);
|
||||
@@ -1835,6 +1878,9 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
||||
SubCommand::FocusMonitor(arg) => {
|
||||
send_message(&SocketMessage::FocusMonitorNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusLastWorkspace => {
|
||||
send_message(&SocketMessage::FocusLastWorkspace.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::FocusWorkspace(arg) => {
|
||||
send_message(&SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?;
|
||||
}
|
||||
@@ -1880,6 +1926,12 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::DisplayIndexPreference(arg) => {
|
||||
send_message(
|
||||
&SocketMessage::DisplayIndexPreference(arg.index_preference, arg.display)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::EnsureWorkspaces(workspaces) => {
|
||||
send_message(
|
||||
&SocketMessage::EnsureWorkspaces(workspaces.monitor, workspaces.workspace_count)
|
||||
@@ -1894,6 +1946,9 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
||||
SubCommand::State => {
|
||||
with_komorebic_socket(|| send_message(&SocketMessage::State.as_bytes()?))?;
|
||||
}
|
||||
SubCommand::VisibleWindows => {
|
||||
with_komorebic_socket(|| send_message(&SocketMessage::VisibleWindows.as_bytes()?))?;
|
||||
}
|
||||
SubCommand::Query(arg) => {
|
||||
with_komorebic_socket(|| {
|
||||
send_message(&SocketMessage::Query(arg.state_query).as_bytes()?)
|
||||
@@ -2015,15 +2070,6 @@ Stop-Process -Name:whkd -ErrorAction SilentlyContinue
|
||||
SubCommand::ActiveWindowBorderOffset(arg) => {
|
||||
send_message(&SocketMessage::ActiveWindowBorderOffset(arg.offset).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::Animation(arg) => {
|
||||
send_message(&SocketMessage::Animation(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationDuration(arg) => {
|
||||
send_message(&SocketMessage::AnimationDuration(arg.duration).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::AnimationEase(arg) => {
|
||||
send_message(&SocketMessage::AnimationEase(arg.ease_func).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user