Compare commits

..

15 Commits

Author SHA1 Message Date
LGUG2Z
7a02b4e28c feat(wm): remove scrolling layout column fallbacks
This commit removes the fallback to the columns layout when the number
of columns on the scrolling layout is less than 3.
2025-10-13 14:56:34 -07:00
LGUG2Z
2ac1929117 feat(wm): add opt to keep focused column centered on scrolling layout
This commit adds a new option under layout_options.scrolling -
"center_focused_column", which defaults to false. When

set to true, and when the number of scrolling columns is an odd number
>=3, komorebi will, if there are enough windows being managed on the
workspace, and if the focused window is not too close to either the
beginning or the end of the workspace ring, keep the focused window in a
centered position in the layout
2025-10-10 17:39:32 -07:00
LGUG2Z
e33a5f28f0 chore(deps): bump windows-rs 2025-10-07 09:43:39 -07:00
LGUG2Z
15d069f2bf chore(deps): bump sysinfo and which 2025-10-07 09:11:20 -07:00
LGUG2Z
3e9947c2e2 ci(github): make sure clippy is installed 2025-10-01 16:04:28 -07:00
LGUG2Z
1c23439f95 chore(deps): bump egui and eframe to 0.32 2025-09-30 18:30:04 -07:00
LGUG2Z
6eb2905e00 refactor(rust): use eyre::Result uniformly
Matching the emerging style in komorebi for Mac
2025-09-30 18:21:14 -07:00
LGUG2Z
52a745d0a3 refactor(rust): upgrade to edition 2024 part 2 (clippy --fix) 2025-09-24 11:15:03 -07:00
LGUG2Z
80877cc449 refactor(rust): upgrade to edition 2024 part 1 2025-09-24 11:09:08 -07:00
LGUG2Z
160cb7202d chore(deps): cargo update 2025-09-20 16:59:07 -07:00
Csaba
8d085df1ba fix(bar): update network interface less frequently
This commit ensures that the default interface for the network widget does not update on every render of the bar.

Some people have experienced the bar being frozen and hopefully this will remediate that issue.
2025-09-20 15:59:59 -07:00
LGUG2Z
5d48a5c5b4 chore(clippy): apply latest fixes 2025-09-20 14:47:35 -07:00
LGUG2Z
539aeec965 refactor(rust): remove getset dependency 2025-09-20 14:34:19 -07:00
LGUG2Z
bcfb058bc3 refactor(rust): standardize on ok_or_eyre and bail!
This commit standardizes the codebase to disallow usage of the raw eyre!
macro for creating errors, instead using ok_or_eyre() when constructing
ad-hoc errors from Result and Option types, and otherwise using the
bail! macro in response to failed boolean conditions.
2025-09-20 12:59:09 -07:00
LGUG2Z
e07b464b0d chore(dev): begin 0.1.39-dev 2025-09-20 12:59:09 -07:00
81 changed files with 3775 additions and 3708 deletions

View File

@@ -47,6 +47,7 @@ jobs:
with:
fetch-depth: 0
- run: rustup toolchain install stable --profile minimal
- run: rustup component add --toolchain stable-x86_64-pc-windows-msvc clippy
- run: rustup toolchain install nightly --allow-downgrade -c rustfmt
- uses: Swatinem/rust-cache@v2
with:

1223
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,8 +19,8 @@ chrono = "0.4"
crossbeam-channel = "0.5"
crossbeam-utils = "0.8"
color-eyre = "0.6"
eframe = "0.31"
egui_extras = "0.31"
eframe = "0.32"
egui_extras = "0.32"
dirs = "6"
dunce = "1"
hotwatch = "0.5"
@@ -35,24 +35,24 @@ tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
parking_lot = "0.12"
paste = "1"
sysinfo = "0.34"
sysinfo = "0.37"
uds_windows = "1"
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "a28c6559a9de2f92c142a714947a9b081776caca" }
windows-numerics = { version = "0.2" }
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "8c42d8db257d30fe95bc98c2e5cd8f75da861021" }
windows-numerics = { version = "0.3" }
windows-implement = { version = "0.60" }
windows-interface = { version = "0.59" }
windows-core = { version = "0.61" }
windows-core = { version = "0.62" }
shadow-rs = "1"
which = "7"
which = "8"
[workspace.dependencies.windows]
version = "0.61"
version = "0.62"
features = [
"Foundation_Numerics",
"Win32_Devices",
"Win32_Devices_Display",
"Win32_System_Com",
"Win32_UI_Shell_Common", # for IObjectArray
"Win32_UI_Shell_Common", # for IObjectArray
"Win32_Foundation",
"Win32_Globalization",
"Win32_Graphics_Dwm",

View File

@@ -107,10 +107,10 @@ unknown-git = "deny"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = [
"https://github.com/LGUG2Z/base16-egui-themes",
"https://github.com/LGUG2Z/catppuccin-egui",
"https://github.com/LGUG2Z/windows-icons",
"https://github.com/LGUG2Z/win32-display-data",
"https://github.com/LGUG2Z/flavours",
"https://github.com/LGUG2Z/base16_color_scheme",
"https://github.com/LGUG2Z/whkd",
# "https://github.com/LGUG2Z/catppuccin-egui",
]

View File

@@ -5,34 +5,34 @@
[
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"adler2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"win32-display-data 0.1.0 git+https://github.com/LGUG2Z/win32-display-data?rev=a28c6559a9de2f92c142a714947a9b081776caca"
"win32-display-data 0.1.0 git+https://github.com/LGUG2Z/win32-display-data?rev=8c42d8db257d30fe95bc98c2e5cd8f75da861021"
]
],
[
"Apache-2.0",
[
"ab_glyph 0.2.31 registry+https://github.com/rust-lang/crates.io-index",
"ab_glyph 0.2.32 registry+https://github.com/rust-lang/crates.io-index",
"ab_glyph_rasterizer 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"accesskit 0.17.1 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_consumer 0.26.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_windows 0.24.1 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_winit 0.23.1 registry+https://github.com/rust-lang/crates.io-index",
"accesskit 0.19.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_consumer 0.28.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_windows 0.27.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_winit 0.27.0 registry+https://github.com/rust-lang/crates.io-index",
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"adler2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"ahash 0.8.12 registry+https://github.com/rust-lang/crates.io-index",
"allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
"anstream 0.6.20 registry+https://github.com/rust-lang/crates.io-index",
"anstyle 1.0.11 registry+https://github.com/rust-lang/crates.io-index",
"anstream 0.6.21 registry+https://github.com/rust-lang/crates.io-index",
"anstyle 1.0.13 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-parse 0.2.7 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-query 1.1.4 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-wincon 3.0.10 registry+https://github.com/rust-lang/crates.io-index",
"anyhow 1.0.99 registry+https://github.com/rust-lang/crates.io-index",
"anyhow 1.0.100 registry+https://github.com/rust-lang/crates.io-index",
"approx 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"arboard 3.6.1 registry+https://github.com/rust-lang/crates.io-index",
"arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
"atomic-waker 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
"autocfg 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"backtrace 0.3.75 registry+https://github.com/rust-lang/crates.io-index",
"backtrace 0.3.76 registry+https://github.com/rust-lang/crates.io-index",
"backtrace-ext 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"base16_color_scheme 0.3.2 git+https://github.com/LGUG2Z/base16_color_scheme",
"base64 0.22.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -41,15 +41,15 @@
"bitflags 1.3.2 registry+https://github.com/rust-lang/crates.io-index",
"bitflags 2.9.4 registry+https://github.com/rust-lang/crates.io-index",
"bitstream-io 2.6.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.23.2 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"cc 1.2.36 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.24.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"cc 1.2.40 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
"chrono 0.4.42 registry+https://github.com/rust-lang/crates.io-index",
"chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.5.48 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.5.48 registry+https://github.com/rust-lang/crates.io-index",
"clap_derive 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap_lex 0.7.5 registry+https://github.com/rust-lang/crates.io-index",
"color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index",
@@ -63,7 +63,7 @@
"ctrlc 3.5.0 registry+https://github.com/rust-lang/crates.io-index",
"cursor-icon 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"deflate 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
"deranged 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"deranged 0.5.4 registry+https://github.com/rust-lang/crates.io-index",
"dirs 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
"dirs 4.0.0 registry+https://github.com/rust-lang/crates.io-index",
"dirs 6.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -74,28 +74,28 @@
"dpi 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
"dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"dyn-clone 1.0.20 registry+https://github.com/rust-lang/crates.io-index",
"ecolor 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"eframe 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui-phosphor 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
"egui-winit 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui_extras 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui_glow 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"ecolor 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"eframe 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui-phosphor 0.10.0 registry+https://github.com/rust-lang/crates.io-index",
"egui-winit 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui_extras 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui_glow 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"either 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
"emath 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"emath 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
"enum-map 2.7.3 registry+https://github.com/rust-lang/crates.io-index",
"enum-map-derive 0.17.0 registry+https://github.com/rust-lang/crates.io-index",
"env_home 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"epaint 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"epaint 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"epaint_default_fonts 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"eyre 0.6.12 registry+https://github.com/rust-lang/crates.io-index",
"fastrand 2.3.0 registry+https://github.com/rust-lang/crates.io-index",
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
"filetime 0.2.26 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"flate2 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"flate2 1.1.4 registry+https://github.com/rust-lang/crates.io-index",
"fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index",
"form_urlencoded 1.2.2 registry+https://github.com/rust-lang/crates.io-index",
"futures 0.3.31 registry+https://github.com/rust-lang/crates.io-index",
@@ -123,6 +123,7 @@
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -130,17 +131,16 @@
"http 1.3.1 registry+https://github.com/rust-lang/crates.io-index",
"httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"iana-time-zone 0.1.63 registry+https://github.com/rust-lang/crates.io-index",
"iana-time-zone 0.1.64 registry+https://github.com/rust-lang/crates.io-index",
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
"idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"idna_adapter 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"image 0.25.8 registry+https://github.com/rust-lang/crates.io-index",
"image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"immutable-chunkmap 2.1.0 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.11.4 registry+https://github.com/rust-lang/crates.io-index",
"ipnet 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"iri-string 0.7.8 registry+https://github.com/rust-lang/crates.io-index",
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -152,12 +152,12 @@
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"khronos_api 3.1.0 registry+https://github.com/rust-lang/crates.io-index",
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.175 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.176 registry+https://github.com/rust-lang/crates.io-index",
"libgit2-sys 0.18.2+1.9.1 registry+https://github.com/rust-lang/crates.io-index",
"libz-sys 1.1.22 registry+https://github.com/rust-lang/crates.io-index",
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
"litrs 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.13 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index",
"log 0.4.28 registry+https://github.com/rust-lang/crates.io-index",
"logos 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
"logos-codegen 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
@@ -168,8 +168,8 @@
"minimal-lexical 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index",
"miow 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"moxcms 0.7.5 registry+https://github.com/rust-lang/crates.io-index",
"miow 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"moxcms 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
"native-tls 0.2.14 registry+https://github.com/rust-lang/crates.io-index",
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
"nohash-hasher 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -189,8 +189,8 @@
"owned_ttf_parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.4 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.11 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index",
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
"percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index",
"pin-project-lite 0.2.16 registry+https://github.com/rust-lang/crates.io-index",
@@ -200,16 +200,14 @@
"png 0.18.0 registry+https://github.com/rust-lang/crates.io-index",
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"ppv-lite86 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro-error-attr2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro-error2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro2 1.0.101 registry+https://github.com/rust-lang/crates.io-index",
"profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.23 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.24 registry+https://github.com/rust-lang/crates.io-index",
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.41 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.9.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -223,8 +221,8 @@
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"rayon 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index",
"regex 1.11.2 registry+https://github.com/rust-lang/crates.io-index",
"regex-automata 0.4.10 registry+https://github.com/rust-lang/crates.io-index",
"regex 1.11.3 registry+https://github.com/rust-lang/crates.io-index",
"regex-automata 0.4.11 registry+https://github.com/rust-lang/crates.io-index",
"regex-syntax 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
"reqwest 0.12.23 registry+https://github.com/rust-lang/crates.io-index",
"roxmltree 0.20.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -232,18 +230,19 @@
"rustls-pki-types 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"ryu 1.0.20 registry+https://github.com/rust-lang/crates.io-index",
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive_internals 0.29.1 registry+https://github.com/rust-lang/crates.io-index",
"serde_json 1.0.143 registry+https://github.com/rust-lang/crates.io-index",
"serde_json 1.0.145 registry+https://github.com/rust-lang/crates.io-index",
"serde_json_lenient 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"serde_urlencoded 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
"serde_variant 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"serde_with 3.14.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with_macros 3.14.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with 3.15.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with_macros 3.15.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_yaml 0.8.26 registry+https://github.com/rust-lang/crates.io-index",
"serde_yaml 0.9.34+deprecated registry+https://github.com/rust-lang/crates.io-index",
"shadow-rs 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
"shadow-rs 1.4.0 registry+https://github.com/rust-lang/crates.io-index",
"shell-words 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
"shlex 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -253,7 +252,7 @@
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"socket2 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"supports-color 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
"supports-hyperlinks 3.1.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -261,27 +260,27 @@
"syn 1.0.109 registry+https://github.com/rust-lang/crates.io-index",
"syn 2.0.106 registry+https://github.com/rust-lang/crates.io-index",
"sync_wrapper 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.21.0 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.23.0 registry+https://github.com/rust-lang/crates.io-index",
"terminal_size 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.16 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.17 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.16 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.17 registry+https://github.com/rust-lang/crates.io-index",
"thread_local 1.1.9 registry+https://github.com/rust-lang/crates.io-index",
"time 0.3.43 registry+https://github.com/rust-lang/crates.io-index",
"time 0.3.44 registry+https://github.com/rust-lang/crates.io-index",
"time-core 0.1.6 registry+https://github.com/rust-lang/crates.io-index",
"toml 0.5.11 registry+https://github.com/rust-lang/crates.io-index",
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.18.0 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.19.0 registry+https://github.com/rust-lang/crates.io-index",
"tz-rs 0.7.0 registry+https://github.com/rust-lang/crates.io-index",
"tzdb 0.7.2 registry+https://github.com/rust-lang/crates.io-index",
"tzdb_data 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"unicase 2.8.1 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.18 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.19 registry+https://github.com/rust-lang/crates.io-index",
"unicode-linebreak 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
"unicode-segmentation 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
"uom 0.37.0 registry+https://github.com/rust-lang/crates.io-index",
"url 2.5.7 registry+https://github.com/rust-lang/crates.io-index",
@@ -297,55 +296,63 @@
"windows 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.61.3 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.62.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.60.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.62.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.59.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.59.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-registry 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.53.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.53.5 registry+https://github.com/rust-lang/crates.io-index",
"windows-threading 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-threading 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"winit 0.30.12 registry+https://github.com/rust-lang/crates.io-index",
"wmi 0.15.2 registry+https://github.com/rust-lang/crates.io-index",
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy 0.8.27 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index",
"zune-core 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
"zune-jpeg 0.4.21 registry+https://github.com/rust-lang/crates.io-index"
@@ -378,8 +385,8 @@
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
"exr 1.73.0 registry+https://github.com/rust-lang/crates.io-index",
"lebe 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"moxcms 0.7.5 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.23 registry+https://github.com/rust-lang/crates.io-index",
"moxcms 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
"pxfm 0.1.24 registry+https://github.com/rust-lang/crates.io-index",
"ravif 0.11.20 registry+https://github.com/rust-lang/crates.io-index"
]
],
@@ -396,7 +403,7 @@
[
"dunce 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"file-id 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"imgref 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"notify 6.1.1 registry+https://github.com/rust-lang/crates.io-index",
"notify-debouncer-full 0.1.0 registry+https://github.com/rust-lang/crates.io-index"
]
@@ -405,34 +412,34 @@
"ISC",
[
"is_ci 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"libloading 0.8.8 registry+https://github.com/rust-lang/crates.io-index",
"libloading 0.8.9 registry+https://github.com/rust-lang/crates.io-index",
"starship-battery 0.10.2 registry+https://github.com/rust-lang/crates.io-index"
]
],
[
"MIT",
[
"accesskit 0.17.1 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_consumer 0.26.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_windows 0.24.1 registry+https://github.com/rust-lang/crates.io-index",
"accesskit 0.19.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_consumer 0.28.0 registry+https://github.com/rust-lang/crates.io-index",
"accesskit_windows 0.27.0 registry+https://github.com/rust-lang/crates.io-index",
"adler 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
"adler2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"ahash 0.8.12 registry+https://github.com/rust-lang/crates.io-index",
"aho-corasick 1.1.3 registry+https://github.com/rust-lang/crates.io-index",
"aligned-vec 0.6.4 registry+https://github.com/rust-lang/crates.io-index",
"allocator-api2 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
"anstream 0.6.20 registry+https://github.com/rust-lang/crates.io-index",
"anstyle 1.0.11 registry+https://github.com/rust-lang/crates.io-index",
"anstream 0.6.21 registry+https://github.com/rust-lang/crates.io-index",
"anstyle 1.0.13 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-parse 0.2.7 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-query 1.1.4 registry+https://github.com/rust-lang/crates.io-index",
"anstyle-wincon 3.0.10 registry+https://github.com/rust-lang/crates.io-index",
"anyhow 1.0.99 registry+https://github.com/rust-lang/crates.io-index",
"anyhow 1.0.100 registry+https://github.com/rust-lang/crates.io-index",
"arboard 3.6.1 registry+https://github.com/rust-lang/crates.io-index",
"arg_enum_proc_macro 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"arrayvec 0.7.6 registry+https://github.com/rust-lang/crates.io-index",
"atomic-waker 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
"autocfg 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"backtrace 0.3.75 registry+https://github.com/rust-lang/crates.io-index",
"backtrace 0.3.76 registry+https://github.com/rust-lang/crates.io-index",
"backtrace-ext 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"base16_color_scheme 0.3.2 git+https://github.com/LGUG2Z/base16_color_scheme",
"base64 0.22.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -444,23 +451,23 @@
"brotli 8.0.2 registry+https://github.com/rust-lang/crates.io-index",
"brotli-decompressor 5.0.0 registry+https://github.com/rust-lang/crates.io-index",
"built 0.7.7 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.23.2 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.24.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"byteorder 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"byteorder-lite 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"bytes 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"calm_io 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"calmio_filters 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"catppuccin-egui 5.3.1 git+https://github.com/LGUG2Z/catppuccin-egui?rev=bdaff30959512c4f7ee7304117076a48633d777f",
"cc 1.2.36 registry+https://github.com/rust-lang/crates.io-index",
"catppuccin-egui 5.6.0 registry+https://github.com/rust-lang/crates.io-index",
"cc 1.2.40 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"cfg-if 1.0.3 registry+https://github.com/rust-lang/crates.io-index",
"cfg_aliases 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"chrono 0.4.42 registry+https://github.com/rust-lang/crates.io-index",
"chrono-tz 0.10.4 registry+https://github.com/rust-lang/crates.io-index",
"chumsky 0.9.3 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap 4.5.48 registry+https://github.com/rust-lang/crates.io-index",
"clap_builder 4.5.48 registry+https://github.com/rust-lang/crates.io-index",
"clap_derive 4.5.47 registry+https://github.com/rust-lang/crates.io-index",
"clap_lex 0.7.5 registry+https://github.com/rust-lang/crates.io-index",
"color-eyre 0.6.5 registry+https://github.com/rust-lang/crates.io-index",
@@ -475,11 +482,11 @@
"crossbeam-utils 0.8.21 registry+https://github.com/rust-lang/crates.io-index",
"ctrlc 3.5.0 registry+https://github.com/rust-lang/crates.io-index",
"cursor-icon 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"darling 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
"darling_core 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
"darling_macro 0.20.11 registry+https://github.com/rust-lang/crates.io-index",
"darling 0.21.3 registry+https://github.com/rust-lang/crates.io-index",
"darling_core 0.21.3 registry+https://github.com/rust-lang/crates.io-index",
"darling_macro 0.21.3 registry+https://github.com/rust-lang/crates.io-index",
"deflate 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
"deranged 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"deranged 0.5.4 registry+https://github.com/rust-lang/crates.io-index",
"dirs 3.0.2 registry+https://github.com/rust-lang/crates.io-index",
"dirs 4.0.0 registry+https://github.com/rust-lang/crates.io-index",
"dirs 6.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -489,21 +496,21 @@
"document-features 0.2.11 registry+https://github.com/rust-lang/crates.io-index",
"dpi 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
"dyn-clone 1.0.20 registry+https://github.com/rust-lang/crates.io-index",
"ecolor 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"eframe 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui-phosphor 0.9.0 registry+https://github.com/rust-lang/crates.io-index",
"egui-winit 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui_extras 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"egui_glow 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"ecolor 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"eframe 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui-phosphor 0.10.0 registry+https://github.com/rust-lang/crates.io-index",
"egui-winit 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui_extras 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"egui_glow 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"either 1.15.0 registry+https://github.com/rust-lang/crates.io-index",
"emath 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"emath 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"encoding_rs 0.8.35 registry+https://github.com/rust-lang/crates.io-index",
"enum-map 2.7.3 registry+https://github.com/rust-lang/crates.io-index",
"enum-map-derive 0.17.0 registry+https://github.com/rust-lang/crates.io-index",
"env_home 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"epaint 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index",
"epaint 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"epaint_default_fonts 0.32.3 registry+https://github.com/rust-lang/crates.io-index",
"equator 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"equator-macro 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"equivalent 1.0.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -513,8 +520,8 @@
"fax_derive 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"fdeflate 0.3.7 registry+https://github.com/rust-lang/crates.io-index",
"filetime 0.2.26 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"flate2 1.1.2 registry+https://github.com/rust-lang/crates.io-index",
"find-msvc-tools 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"flate2 1.1.4 registry+https://github.com/rust-lang/crates.io-index",
"flavours 0.7.2 git+https://github.com/LGUG2Z/flavours",
"fnv 1.0.7 registry+https://github.com/rust-lang/crates.io-index",
"font-loader 0.11.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -532,7 +539,6 @@
"getrandom 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.2.16 registry+https://github.com/rust-lang/crates.io-index",
"getrandom 0.3.3 registry+https://github.com/rust-lang/crates.io-index",
"getset 0.1.6 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.11.4 registry+https://github.com/rust-lang/crates.io-index",
"gif 0.13.3 registry+https://github.com/rust-lang/crates.io-index",
"git2 0.20.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -544,6 +550,7 @@
"hashbrown 0.12.3 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.14.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.15.5 registry+https://github.com/rust-lang/crates.io-index",
"hashbrown 0.16.0 registry+https://github.com/rust-lang/crates.io-index",
"heck 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"hex 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"hex_color 3.0.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -554,18 +561,17 @@
"httparse 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"hyper 1.7.0 registry+https://github.com/rust-lang/crates.io-index",
"hyper-tls 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"hyper-util 0.1.16 registry+https://github.com/rust-lang/crates.io-index",
"iana-time-zone 0.1.63 registry+https://github.com/rust-lang/crates.io-index",
"hyper-util 0.1.17 registry+https://github.com/rust-lang/crates.io-index",
"iana-time-zone 0.1.64 registry+https://github.com/rust-lang/crates.io-index",
"ident_case 1.0.1 registry+https://github.com/rust-lang/crates.io-index",
"idna 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"idna_adapter 1.2.1 registry+https://github.com/rust-lang/crates.io-index",
"image 0.23.14 registry+https://github.com/rust-lang/crates.io-index",
"image 0.25.8 registry+https://github.com/rust-lang/crates.io-index",
"image-webp 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"immutable-chunkmap 2.1.0 registry+https://github.com/rust-lang/crates.io-index",
"indenter 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 1.9.3 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"indexmap 2.11.4 registry+https://github.com/rust-lang/crates.io-index",
"ipnet 2.11.0 registry+https://github.com/rust-lang/crates.io-index",
"iri-string 0.7.8 registry+https://github.com/rust-lang/crates.io-index",
"is_debug 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -576,12 +582,12 @@
"jobserver 0.1.34 registry+https://github.com/rust-lang/crates.io-index",
"jpeg-decoder 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"lazy_static 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.175 registry+https://github.com/rust-lang/crates.io-index",
"libc 0.2.176 registry+https://github.com/rust-lang/crates.io-index",
"libgit2-sys 0.18.2+1.9.1 registry+https://github.com/rust-lang/crates.io-index",
"libz-sys 1.1.22 registry+https://github.com/rust-lang/crates.io-index",
"linked-hash-map 0.5.6 registry+https://github.com/rust-lang/crates.io-index",
"litrs 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.13 registry+https://github.com/rust-lang/crates.io-index",
"lock_api 0.4.14 registry+https://github.com/rust-lang/crates.io-index",
"log 0.4.28 registry+https://github.com/rust-lang/crates.io-index",
"logos 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
"logos-codegen 0.14.4 registry+https://github.com/rust-lang/crates.io-index",
@@ -589,7 +595,7 @@
"loop9 0.1.5 registry+https://github.com/rust-lang/crates.io-index",
"matchers 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"maybe-rayon 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"memchr 2.7.5 registry+https://github.com/rust-lang/crates.io-index",
"memchr 2.7.6 registry+https://github.com/rust-lang/crates.io-index",
"memoffset 0.9.1 registry+https://github.com/rust-lang/crates.io-index",
"mime 0.3.17 registry+https://github.com/rust-lang/crates.io-index",
"mime_guess2 2.3.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -598,7 +604,7 @@
"miniz_oxide 0.4.4 registry+https://github.com/rust-lang/crates.io-index",
"miniz_oxide 0.8.9 registry+https://github.com/rust-lang/crates.io-index",
"mio 1.0.4 registry+https://github.com/rust-lang/crates.io-index",
"miow 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"miow 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"nanoid 0.4.0 registry+https://github.com/rust-lang/crates.io-index",
"native-tls 0.2.14 registry+https://github.com/rust-lang/crates.io-index",
"net2 0.2.39 registry+https://github.com/rust-lang/crates.io-index",
@@ -622,11 +628,11 @@
"once_cell 1.21.3 registry+https://github.com/rust-lang/crates.io-index",
"once_cell_polyfill 1.70.1 registry+https://github.com/rust-lang/crates.io-index",
"os_info 3.12.0 registry+https://github.com/rust-lang/crates.io-index",
"owo-colors 4.2.2 registry+https://github.com/rust-lang/crates.io-index",
"owo-colors 4.2.3 registry+https://github.com/rust-lang/crates.io-index",
"palette 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"palette_derive 0.5.0 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.4 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.11 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot 0.12.5 registry+https://github.com/rust-lang/crates.io-index",
"parking_lot_core 0.9.12 registry+https://github.com/rust-lang/crates.io-index",
"paste 1.0.15 registry+https://github.com/rust-lang/crates.io-index",
"percent-encoding 2.3.2 registry+https://github.com/rust-lang/crates.io-index",
"phf 0.11.3 registry+https://github.com/rust-lang/crates.io-index",
@@ -645,15 +651,13 @@
"powerfmt 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"powershell_script 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"ppv-lite86 0.2.21 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro-error-attr2 2.0.0 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro-error2 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"proc-macro2 1.0.101 registry+https://github.com/rust-lang/crates.io-index",
"profiling 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"profiling-procmacros 1.0.17 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.26 registry+https://github.com/rust-lang/crates.io-index",
"psm 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
"qoi 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"quick-error 2.0.1 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.40 registry+https://github.com/rust-lang/crates.io-index",
"quote 1.0.41 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.7.3 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index",
"rand 0.9.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -668,8 +672,8 @@
"raw-window-handle 0.6.2 registry+https://github.com/rust-lang/crates.io-index",
"rayon 1.11.0 registry+https://github.com/rust-lang/crates.io-index",
"rayon-core 1.13.0 registry+https://github.com/rust-lang/crates.io-index",
"regex 1.11.2 registry+https://github.com/rust-lang/crates.io-index",
"regex-automata 0.4.10 registry+https://github.com/rust-lang/crates.io-index",
"regex 1.11.3 registry+https://github.com/rust-lang/crates.io-index",
"regex-automata 0.4.11 registry+https://github.com/rust-lang/crates.io-index",
"regex-syntax 0.8.6 registry+https://github.com/rust-lang/crates.io-index",
"reqwest 0.12.23 registry+https://github.com/rust-lang/crates.io-index",
"rgb 0.8.52 registry+https://github.com/rust-lang/crates.io-index",
@@ -677,23 +681,24 @@
"rustc-demangle 0.1.26 registry+https://github.com/rust-lang/crates.io-index",
"rustls-pki-types 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"same-file 1.0.6 registry+https://github.com/rust-lang/crates.io-index",
"schannel 0.1.27 registry+https://github.com/rust-lang/crates.io-index",
"schannel 0.1.28 registry+https://github.com/rust-lang/crates.io-index",
"schemars 0.8.22 registry+https://github.com/rust-lang/crates.io-index",
"schemars_derive 0.8.22 registry+https://github.com/rust-lang/crates.io-index",
"scoped_threadpool 0.1.9 registry+https://github.com/rust-lang/crates.io-index",
"scopeguard 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.219 registry+https://github.com/rust-lang/crates.io-index",
"serde 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_core 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive 1.0.228 registry+https://github.com/rust-lang/crates.io-index",
"serde_derive_internals 0.29.1 registry+https://github.com/rust-lang/crates.io-index",
"serde_json 1.0.143 registry+https://github.com/rust-lang/crates.io-index",
"serde_json 1.0.145 registry+https://github.com/rust-lang/crates.io-index",
"serde_json_lenient 0.2.4 registry+https://github.com/rust-lang/crates.io-index",
"serde_urlencoded 0.7.1 registry+https://github.com/rust-lang/crates.io-index",
"serde_variant 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"serde_with 3.14.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with_macros 3.14.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with 3.15.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_with_macros 3.15.0 registry+https://github.com/rust-lang/crates.io-index",
"serde_yaml 0.8.26 registry+https://github.com/rust-lang/crates.io-index",
"serde_yaml 0.9.34+deprecated registry+https://github.com/rust-lang/crates.io-index",
"shadow-rs 1.3.0 registry+https://github.com/rust-lang/crates.io-index",
"shadow-rs 1.4.0 registry+https://github.com/rust-lang/crates.io-index",
"sharded-slab 0.1.7 registry+https://github.com/rust-lang/crates.io-index",
"shell-words 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"shellexpand 2.1.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -707,7 +712,7 @@
"smol_str 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"socket2 0.6.0 registry+https://github.com/rust-lang/crates.io-index",
"stable_deref_trait 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.21 registry+https://github.com/rust-lang/crates.io-index",
"stacker 0.1.22 registry+https://github.com/rust-lang/crates.io-index",
"static_assertions 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"strsim 0.11.1 registry+https://github.com/rust-lang/crates.io-index",
"strum 0.27.2 registry+https://github.com/rust-lang/crates.io-index",
@@ -716,18 +721,18 @@
"syn 2.0.106 registry+https://github.com/rust-lang/crates.io-index",
"synstructure 0.13.2 registry+https://github.com/rust-lang/crates.io-index",
"sysinfo 0.33.1 registry+https://github.com/rust-lang/crates.io-index",
"sysinfo 0.34.2 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.21.0 registry+https://github.com/rust-lang/crates.io-index",
"sysinfo 0.37.2 registry+https://github.com/rust-lang/crates.io-index",
"tempfile 3.23.0 registry+https://github.com/rust-lang/crates.io-index",
"terminal_size 0.4.3 registry+https://github.com/rust-lang/crates.io-index",
"textwrap 0.16.2 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.16 registry+https://github.com/rust-lang/crates.io-index",
"thiserror 2.0.17 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 1.0.69 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.16 registry+https://github.com/rust-lang/crates.io-index",
"thiserror-impl 2.0.17 registry+https://github.com/rust-lang/crates.io-index",
"thread_local 1.1.9 registry+https://github.com/rust-lang/crates.io-index",
"tiff 0.10.3 registry+https://github.com/rust-lang/crates.io-index",
"tiff 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"time 0.3.43 registry+https://github.com/rust-lang/crates.io-index",
"time 0.3.44 registry+https://github.com/rust-lang/crates.io-index",
"time-core 0.1.6 registry+https://github.com/rust-lang/crates.io-index",
"tokio 1.47.1 registry+https://github.com/rust-lang/crates.io-index",
"tokio-native-tls 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
@@ -746,15 +751,15 @@
"tracing-subscriber 0.3.20 registry+https://github.com/rust-lang/crates.io-index",
"try-lock 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
"ttf-parser 0.25.1 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.18.0 registry+https://github.com/rust-lang/crates.io-index",
"typenum 1.19.0 registry+https://github.com/rust-lang/crates.io-index",
"tz-rs 0.7.0 registry+https://github.com/rust-lang/crates.io-index",
"tzdb_data 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"uds_windows 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"unicase 2.8.1 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.18 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.19 registry+https://github.com/rust-lang/crates.io-index",
"unicode-segmentation 1.12.0 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.1.14 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"unicode-width 0.2.2 registry+https://github.com/rust-lang/crates.io-index",
"unicode-xid 0.2.6 registry+https://github.com/rust-lang/crates.io-index",
"unsafe-libyaml 0.2.11 registry+https://github.com/rust-lang/crates.io-index",
"uom 0.37.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -768,59 +773,67 @@
"web-time 1.1.0 registry+https://github.com/rust-lang/crates.io-index",
"webbrowser 1.0.5 registry+https://github.com/rust-lang/crates.io-index",
"weezl 0.1.10 registry+https://github.com/rust-lang/crates.io-index",
"which 7.0.3 registry+https://github.com/rust-lang/crates.io-index",
"which 8.0.0 registry+https://github.com/rust-lang/crates.io-index",
"winapi 0.3.9 registry+https://github.com/rust-lang/crates.io-index",
"winapi-util 0.1.11 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.61.3 registry+https://github.com/rust-lang/crates.io-index",
"windows 0.62.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-collections 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.60.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-core 0.62.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-future 0.3.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-icons 0.1.0 git+https://github.com/LGUG2Z/windows-icons?rev=0c9d7ee1b807347c507d3a9862dd007b4d3f4354",
"windows-icons 0.1.0 git+https://github.com/LGUG2Z/windows-icons?rev=d67cc9920aa9b4883393e411fb4fa2ddd4c498b5",
"windows-implement 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.60.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-implement 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.57.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.58.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.59.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-interface 0.59.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-link 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.1.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-numerics 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-registry 0.5.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.1.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.2.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.3.4 registry+https://github.com/rust-lang/crates.io-index",
"windows-result 0.4.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.3.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.4.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-strings 0.5.1 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.48.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.52.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.59.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.60.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-sys 0.61.2 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.53.3 registry+https://github.com/rust-lang/crates.io-index",
"windows-targets 0.53.5 registry+https://github.com/rust-lang/crates.io-index",
"windows-threading 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"windows-threading 0.2.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_aarch64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_i686_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.48.5 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.52.6 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.53.0 registry+https://github.com/rust-lang/crates.io-index",
"windows_x86_64_msvc 0.53.1 registry+https://github.com/rust-lang/crates.io-index",
"winput 0.2.5 registry+https://github.com/rust-lang/crates.io-index",
"winreg 0.55.0 registry+https://github.com/rust-lang/crates.io-index",
"winsafe 0.0.19 registry+https://github.com/rust-lang/crates.io-index",
@@ -828,7 +841,7 @@
"xml-rs 0.8.27 registry+https://github.com/rust-lang/crates.io-index",
"yaml-rust 0.4.5 registry+https://github.com/rust-lang/crates.io-index",
"zerocopy 0.8.27 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.1 registry+https://github.com/rust-lang/crates.io-index",
"zeroize 1.8.2 registry+https://github.com/rust-lang/crates.io-index",
"zune-core 0.4.12 registry+https://github.com/rust-lang/crates.io-index",
"zune-inflate 0.2.54 registry+https://github.com/rust-lang/crates.io-index",
"zune-jpeg 0.4.21 registry+https://github.com/rust-lang/crates.io-index"
@@ -851,13 +864,13 @@
[
"OFL-1.1",
[
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index"
"epaint_default_fonts 0.32.3 registry+https://github.com/rust-lang/crates.io-index"
]
],
[
"Ubuntu-font-1.0",
[
"epaint_default_fonts 0.31.1 registry+https://github.com/rust-lang/crates.io-index"
"epaint_default_fonts 0.32.3 registry+https://github.com/rust-lang/crates.io-index"
]
],
[
@@ -873,7 +886,7 @@
"litemap 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"potential_utf 0.1.3 registry+https://github.com/rust-lang/crates.io-index",
"tinystr 0.8.1 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.18 registry+https://github.com/rust-lang/crates.io-index",
"unicode-ident 1.0.19 registry+https://github.com/rust-lang/crates.io-index",
"writeable 0.6.1 registry+https://github.com/rust-lang/crates.io-index",
"yoke 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
"yoke-derive 0.8.0 registry+https://github.com/rust-lang/crates.io-index",
@@ -890,7 +903,7 @@
"aho-corasick 1.1.3 registry+https://github.com/rust-lang/crates.io-index",
"byteorder 1.5.0 registry+https://github.com/rust-lang/crates.io-index",
"byteorder-lite 0.1.0 registry+https://github.com/rust-lang/crates.io-index",
"memchr 2.7.5 registry+https://github.com/rust-lang/crates.io-index",
"memchr 2.7.6 registry+https://github.com/rust-lang/crates.io-index",
"same-file 1.0.6 registry+https://github.com/rust-lang/crates.io-index",
"walkdir 2.5.0 registry+https://github.com/rust-lang/crates.io-index",
"winapi-util 0.1.11 registry+https://github.com/rust-lang/crates.io-index"
@@ -900,9 +913,9 @@
"Zlib",
[
"adler32 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.23.2 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.1 registry+https://github.com/rust-lang/crates.io-index",
"const_format 0.2.34 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck 1.24.0 registry+https://github.com/rust-lang/crates.io-index",
"bytemuck_derive 1.10.2 registry+https://github.com/rust-lang/crates.io-index",
"const_format 0.2.35 registry+https://github.com/rust-lang/crates.io-index",
"const_format_proc_macros 0.2.34 registry+https://github.com/rust-lang/crates.io-index",
"cursor-icon 1.2.0 registry+https://github.com/rust-lang/crates.io-index",
"foldhash 0.1.5 registry+https://github.com/rust-lang/crates.io-index",

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.38/schema.bar.json",
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.39/schema.bar.json",
"monitor": 0,
"font_family": "JetBrains Mono",
"theme": {

View File

@@ -1,5 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.38/schema.json",
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.39/schema.json",
"app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
"window_hiding_behaviour": "Cloak",
"cross_monitor_move_behaviour": "Insert",

View File

@@ -1,7 +1,7 @@
[package]
name = "komorebi-bar"
version = "0.1.38"
edition = "2021"
version = "0.1.39"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,7 +17,7 @@ crossbeam-channel = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
eframe = { workspace = true }
egui-phosphor = "0.9"
egui-phosphor = "0.10"
font-loader = "0.11"
hotwatch = { workspace = true }
image = "0.25"

View File

@@ -1,9 +1,18 @@
use crate::config::get_individual_spacing;
use crate::AUTO_SELECT_FILL_COLOUR;
use crate::AUTO_SELECT_TEXT_COLOUR;
use crate::BAR_HEIGHT;
use crate::DEFAULT_PADDING;
use crate::KomorebiEvent;
use crate::MAX_LABEL_WIDTH;
use crate::MONITOR_LEFT;
use crate::MONITOR_RIGHT;
use crate::MONITOR_TOP;
use crate::config::KomobarConfig;
use crate::config::KomobarTheme;
use crate::config::MonitorConfigOrIndex;
use crate::config::Position;
use crate::config::PositionConfig;
use crate::config::get_individual_spacing;
use crate::process_hwnd;
use crate::render::Color32Ext;
use crate::render::Grouping;
@@ -13,15 +22,7 @@ use crate::widgets::komorebi::Komorebi;
use crate::widgets::komorebi::MonitorInfo;
use crate::widgets::widget::BarWidget;
use crate::widgets::widget::WidgetConfig;
use crate::KomorebiEvent;
use crate::AUTO_SELECT_FILL_COLOUR;
use crate::AUTO_SELECT_TEXT_COLOUR;
use crate::BAR_HEIGHT;
use crate::DEFAULT_PADDING;
use crate::MAX_LABEL_WIDTH;
use crate::MONITOR_LEFT;
use crate::MONITOR_RIGHT;
use crate::MONITOR_TOP;
use color_eyre::eyre;
use crossbeam_channel::Receiver;
use crossbeam_channel::TryRecvError;
use eframe::egui::Align;
@@ -52,16 +53,15 @@ use komorebi_client::NotificationEvent;
use komorebi_client::PathExt;
use komorebi_client::SocketMessage;
use komorebi_client::VirtualDesktopNotification;
use komorebi_themes::catppuccin_egui;
use komorebi_themes::Base16Wrapper;
use komorebi_themes::Catppuccin;
use komorebi_themes::catppuccin_egui;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Result;
use std::io::Write;
use std::os::windows::process::CommandExt;
use std::path::PathBuf;
@@ -69,8 +69,8 @@ use std::process::ChildStdin;
use std::process::Command;
use std::process::Stdio;
use std::rc::Rc;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::Ordering;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
@@ -78,7 +78,7 @@ lazy_static! {
static ref SESSION_STDIN: Mutex<Option<ChildStdin>> = Mutex::new(None);
}
fn start_powershell() -> Result<()> {
fn start_powershell() -> eyre::Result<()> {
// found running session, do nothing
if SESSION_STDIN.lock().as_mut().is_some() {
tracing::debug!("PowerShell session already started");
@@ -102,17 +102,17 @@ fn start_powershell() -> Result<()> {
Ok(())
}
fn stop_powershell() -> Result<()> {
fn stop_powershell() -> eyre::Result<()> {
tracing::debug!("Stopping PowerShell session");
if let Some(mut session_stdin) = SESSION_STDIN.lock().take() {
if let Err(e) = session_stdin.write_all(b"exit\n") {
tracing::error!(error = %e, "failed to write exit command to PowerShell stdin");
return Err(e);
return Err(e.into());
}
if let Err(e) = session_stdin.flush() {
tracing::error!(error = %e, "failed to flush PowerShell stdin");
return Err(e);
return Err(e.into());
}
tracing::debug!("PowerShell session stopped");
@@ -123,25 +123,22 @@ fn stop_powershell() -> Result<()> {
Ok(())
}
pub fn exec_powershell(cmd: &str) -> Result<()> {
pub fn exec_powershell(cmd: &str) -> eyre::Result<()> {
if let Some(session_stdin) = SESSION_STDIN.lock().as_mut() {
if let Err(e) = writeln!(session_stdin, "{cmd}") {
tracing::error!(error = %e, cmd = cmd, "failed to write command to PowerShell stdin");
return Err(e);
return Err(e.into());
}
if let Err(e) = session_stdin.flush() {
tracing::error!(error = %e, "failed to flush PowerShell stdin");
return Err(e);
return Err(e.into());
}
return Ok(());
}
Err(Error::new(
ErrorKind::NotFound,
"PowerShell session not started",
))
Err(Error::new(ErrorKind::NotFound, "PowerShell session not started").into())
}
pub struct Komobar {
@@ -322,16 +319,15 @@ pub fn apply_theme(
// apply rounding to the widgets
if let Some(Grouping::Bar(config) | Grouping::Alignment(config) | Grouping::Widget(config)) =
&grouping
&& let Some(rounding) = config.rounding
{
if let Some(rounding) = config.rounding {
ctx.style_mut(|style| {
style.visuals.widgets.noninteractive.corner_radius = rounding.into();
style.visuals.widgets.inactive.corner_radius = rounding.into();
style.visuals.widgets.hovered.corner_radius = rounding.into();
style.visuals.widgets.active.corner_radius = rounding.into();
style.visuals.widgets.open.corner_radius = rounding.into();
});
}
ctx.style_mut(|style| {
style.visuals.widgets.noninteractive.corner_radius = rounding.into();
style.visuals.widgets.inactive.corner_radius = rounding.into();
style.visuals.widgets.hovered.corner_radius = rounding.into();
style.visuals.widgets.active.corner_radius = rounding.into();
style.visuals.widgets.open.corner_radius = rounding.into();
});
}
// Update RenderConfig's background_color so that widgets will have the new color
@@ -523,10 +519,14 @@ impl Komobar {
}
}
} else if self.monitor_info.is_some() && !self.disabled {
tracing::warn!("couldn't find the monitor index of this bar! Disabling the bar until the monitor connects...");
tracing::warn!(
"couldn't find the monitor index of this bar! Disabling the bar until the monitor connects..."
);
self.disabled = true;
} else {
tracing::warn!("couldn't find the monitor index of this bar, if the bar is starting up this is normal until it receives the first state from komorebi.");
tracing::warn!(
"couldn't find the monitor index of this bar, if the bar is starting up this is normal until it receives the first state from komorebi."
);
self.disabled = true;
}
@@ -599,7 +599,9 @@ impl Komobar {
end.x -= margin.left + margin.right;
if end.y == 0.0 {
tracing::warn!("position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default")
tracing::warn!(
"position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default"
)
}
self.size_rect = komorebi_client::Rect {
@@ -666,17 +668,16 @@ impl Komobar {
| Grouping::Alignment(config)
| Grouping::Widget(config),
) = &bar_grouping
&& let Some(rounding) = config.rounding
{
if let Some(rounding) = config.rounding {
ctx.style_mut(|style| {
style.visuals.widgets.noninteractive.corner_radius =
rounding.into();
style.visuals.widgets.inactive.corner_radius = rounding.into();
style.visuals.widgets.hovered.corner_radius = rounding.into();
style.visuals.widgets.active.corner_radius = rounding.into();
style.visuals.widgets.open.corner_radius = rounding.into();
});
}
ctx.style_mut(|style| {
style.visuals.widgets.noninteractive.corner_radius =
rounding.into();
style.visuals.widgets.inactive.corner_radius = rounding.into();
style.visuals.widgets.hovered.corner_radius = rounding.into();
style.visuals.widgets.active.corner_radius = rounding.into();
style.visuals.widgets.open.corner_radius = rounding.into();
});
}
}
}
@@ -937,9 +938,9 @@ impl eframe::App for Komobar {
) {
let monitor_index = self.monitor_index.expect("should have a monitor index");
let monitor_size = state.monitors.elements()[monitor_index].size();
let monitor_size = state.monitors.elements()[monitor_index].size;
self.update_monitor_coordinates(monitor_size);
self.update_monitor_coordinates(&monitor_size);
should_apply_config = true;
}
@@ -950,7 +951,7 @@ impl eframe::App for Komobar {
// Check if monitor coordinates/size has changed
if let Some(monitor_index) = self.monitor_index {
let monitor_size = state.monitors.elements()[monitor_index].size();
let monitor_size = state.monitors.elements()[monitor_index].size;
let top = MONITOR_TOP.load(Ordering::SeqCst);
let left = MONITOR_LEFT.load(Ordering::SeqCst);
let right = MONITOR_RIGHT.load(Ordering::SeqCst);
@@ -960,13 +961,13 @@ impl eframe::App for Komobar {
bottom: monitor_size.bottom,
right,
};
if *monitor_size != rect {
if monitor_size != rect {
tracing::info!(
"Monitor coordinates/size has changed, storing new coordinates: {:#?}",
monitor_size
);
self.update_monitor_coordinates(monitor_size);
self.update_monitor_coordinates(&monitor_size);
should_apply_config = true;
}
@@ -1358,7 +1359,9 @@ fn handle_notification(
"removed theme from updated komorebi.json and applied default theme"
);
} else {
tracing::warn!("theme was removed from updated komorebi.json but there was no default theme to apply");
tracing::warn!(
"theme was removed from updated komorebi.json but there was no default theme to apply"
);
}
}
}

View File

@@ -1,7 +1,7 @@
use crate::DEFAULT_PADDING;
use crate::bar::exec_powershell;
use crate::render::Grouping;
use crate::widgets::widget::WidgetConfig;
use crate::DEFAULT_PADDING;
use eframe::egui::Pos2;
use eframe::egui::TextBuffer;
use eframe::egui::Vec2;
@@ -16,7 +16,7 @@ use std::path::PathBuf;
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
/// The `komorebi.bar.json` configuration file reference for `v0.1.38`
/// The `komorebi.bar.json` configuration file reference for `v0.1.39`
pub struct KomobarConfig {
/// Bar height (default: 50)
pub height: Option<f32>,
@@ -120,7 +120,9 @@ impl KomobarConfig {
}
if display {
println!("\nYour bar configuration file contains some options that have been renamed or deprecated:\n");
println!(
"\nYour bar configuration file contains some options that have been renamed or deprecated:\n"
);
for (canonical, aliases) in map {
for alias in aliases {
if raw.contains(alias) {
@@ -453,7 +455,7 @@ impl MouseMessage {
tracing::debug!("Sending messages: {messages:?}");
if komorebi_client::send_batch(messages.into_iter()).is_err() {
if komorebi_client::send_batch(messages).is_err() {
tracing::error!("could not send commands");
}
}

View File

@@ -15,10 +15,10 @@ use eframe::egui::ViewportBuilder;
use font_loader::system_fonts;
use hotwatch::EventKind;
use hotwatch::Hotwatch;
use komorebi_client::replace_env_in_path;
use komorebi_client::PathExt;
use komorebi_client::SocketMessage;
use komorebi_client::SubscribeOptions;
use komorebi_client::replace_env_in_path;
use std::io::BufReader;
use std::io::Read;
use std::path::PathBuf;
@@ -32,8 +32,8 @@ use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::System::Threading::GetCurrentProcessId;
use windows::Win32::System::Threading::GetCurrentThreadId;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows;
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use windows_core::BOOL;
@@ -114,14 +114,14 @@ fn main() -> color_eyre::Result<()> {
#[cfg(feature = "schemars")]
if opts.schema {
let settings = schemars::gen::SchemaSettings::default().with(|s| {
let settings = schemars::r#gen::SchemaSettings::default().with(|s| {
s.option_nullable = false;
s.option_add_null_type = false;
s.inline_subschemas = true;
});
let gen = settings.into_generator();
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
let generator = settings.into_generator();
let socket_message = generator.into_root_schema_for::<KomobarConfig>();
let schema = serde_json::to_string_pretty(&socket_message)?;
println!("{schema}");
@@ -137,13 +137,17 @@ fn main() -> color_eyre::Result<()> {
}
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
unsafe {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
}
}
color_eyre::install()?;
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info");
unsafe {
std::env::set_var("RUST_LOG", "info");
}
}
tracing::subscriber::set_global_default(
@@ -230,17 +234,17 @@ fn main() -> color_eyre::Result<()> {
.map_or(usr_monitor_index, |i| *i);
MONITOR_RIGHT.store(
state.monitors.elements()[monitor_index].size().right,
state.monitors.elements()[monitor_index].size.right,
Ordering::SeqCst,
);
MONITOR_TOP.store(
state.monitors.elements()[monitor_index].size().top,
state.monitors.elements()[monitor_index].size.top,
Ordering::SeqCst,
);
MONITOR_LEFT.store(
state.monitors.elements()[monitor_index].size().left,
state.monitors.elements()[monitor_index].size.left,
Ordering::SeqCst,
);
@@ -250,11 +254,11 @@ fn main() -> color_eyre::Result<()> {
None => {
config.position = Some(PositionConfig {
start: Some(Position {
x: state.monitors.elements()[monitor_index].size().left as f32,
y: state.monitors.elements()[monitor_index].size().top as f32,
x: state.monitors.elements()[monitor_index].size.left as f32,
y: state.monitors.elements()[monitor_index].size.top as f32,
}),
end: Some(Position {
x: state.monitors.elements()[monitor_index].size().right as f32,
x: state.monitors.elements()[monitor_index].size.right as f32,
y: 50.0,
}),
})
@@ -262,14 +266,14 @@ fn main() -> color_eyre::Result<()> {
Some(ref mut position) => {
if position.start.is_none() {
position.start = Some(Position {
x: state.monitors.elements()[monitor_index].size().left as f32,
y: state.monitors.elements()[monitor_index].size().top as f32,
x: state.monitors.elements()[monitor_index].size.left as f32,
y: state.monitors.elements()[monitor_index].size.top as f32,
});
}
if position.end.is_none() {
position.end = Some(Position {
x: state.monitors.elements()[monitor_index].size().right as f32,
x: state.monitors.elements()[monitor_index].size.right as f32,
y: 50.0,
})
}
@@ -354,7 +358,7 @@ fn main() -> color_eyre::Result<()> {
while komorebi_client::send_message(
&SocketMessage::AddSubscriberSocket(subscriber_name.clone()),
)
.is_err()
.is_err()
{
std::thread::sleep(Duration::from_secs(1));
}
@@ -405,5 +409,5 @@ fn main() -> color_eyre::Result<()> {
Ok(Box::new(Komobar::new(cc, rx_gui, rx_config, config)))
}),
)
.map_err(|error| color_eyre::eyre::Error::msg(error.to_string()))
.map_err(|error| color_eyre::eyre::Error::msg(error.to_string()))
}

View File

@@ -1,8 +1,8 @@
use crate::AUTO_SELECT_FILL_COLOUR;
use crate::AUTO_SELECT_TEXT_COLOUR;
use crate::bar::Alignment;
use crate::config::KomobarConfig;
use crate::config::MonitorConfigOrIndex;
use crate::AUTO_SELECT_FILL_COLOUR;
use crate::AUTO_SELECT_TEXT_COLOUR;
use eframe::egui::Color32;
use eframe::egui::Context;
use eframe::egui::CornerRadius;
@@ -18,9 +18,9 @@ use komorebi_client::Rgb;
use serde::Deserialize;
use serde::Serialize;
use std::num::NonZeroU32;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
static SHOW_KOMOREBI_LAYOUT_OPTIONS: AtomicUsize = AtomicUsize::new(0);

View File

@@ -2,7 +2,6 @@ use super::ImageIcon;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::vec2;
use eframe::egui::Color32;
use eframe::egui::Context;
use eframe::egui::CornerRadius;
@@ -17,6 +16,7 @@ use eframe::egui::Stroke;
use eframe::egui::StrokeKind;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::egui::vec2;
use komorebi_client::PathExt;
use serde::Deserialize;
use serde::Serialize;

View File

@@ -2,17 +2,17 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use starship_battery::units::ratio::percent;
use starship_battery::Manager;
use starship_battery::State;
use starship_battery::units::ratio::percent;
use std::process::Command;
use std::time::Duration;
use std::time::Instant;
@@ -87,41 +87,41 @@ impl Battery {
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
output = None;
if let Ok(mut batteries) = self.manager.batteries() {
if let Some(Ok(first)) = batteries.nth(0) {
let percentage = first.state_of_charge().get::<percent>().round() as u8;
if let Ok(mut batteries) = self.manager.batteries()
&& let Some(Ok(first)) = batteries.nth(0)
{
let percentage = first.state_of_charge().get::<percent>().round() as u8;
if percentage == 100 && self.hide_on_full_charge {
output = None
} else {
match first.state() {
State::Charging => self.state = BatteryState::Charging,
State::Discharging => {
self.state = match percentage {
p if p > 75 => BatteryState::Discharging,
p if p > 50 => BatteryState::High,
p if p > 25 => BatteryState::Medium,
p if p > 10 => BatteryState::Low,
_ => BatteryState::Warning,
}
if percentage == 100 && self.hide_on_full_charge {
output = None
} else {
match first.state() {
State::Charging => self.state = BatteryState::Charging,
State::Discharging => {
self.state = match percentage {
p if p > 75 => BatteryState::Discharging,
p if p > 50 => BatteryState::High,
p if p > 25 => BatteryState::Medium,
p if p > 10 => BatteryState::Low,
_ => BatteryState::Warning,
}
_ => {}
}
let selected = self.auto_select_under.is_some_and(|u| percentage <= u);
output = Some(BatteryOutput {
label: match self.label_prefix {
LabelPrefix::Text | LabelPrefix::IconAndText => {
format!("BAT: {percentage}%")
}
LabelPrefix::None | LabelPrefix::Icon => {
format!("{percentage}%")
}
},
selected,
})
_ => {}
}
let selected = self.auto_select_under.is_some_and(|u| percentage <= u);
output = Some(BatteryOutput {
label: match self.label_prefix {
LabelPrefix::Text | LabelPrefix::IconAndText => {
format!("BAT: {percentage}%")
}
LabelPrefix::None | LabelPrefix::Icon => {
format!("{percentage}%")
}
},
selected,
})
}
}
@@ -176,13 +176,11 @@ impl BarWidget for Battery {
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
.clicked()
{
if let Err(error) = Command::new("cmd.exe")
&& let Err(error) = Command::new("cmd.exe")
.args(["/C", "start", "ms-settings:batterysaver"])
.spawn()
{
eprintln!("{error}")
}
{
eprintln!("{error}")
}
});
}

View File

@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::process::Command;
@@ -120,12 +120,10 @@ impl BarWidget for Cpu {
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
.clicked()
{
if let Err(error) =
&& let Err(error) =
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
{
eprintln!("{error}")
}
{
eprintln!("{error}")
}
});
}

View File

@@ -4,15 +4,16 @@ use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use chrono::Local;
use chrono_tz::Tz;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::WidgetText;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
@@ -225,7 +226,7 @@ impl BarWidget for Date {
if SelectableFrame::new(false)
.show(ui, |ui| {
ui.add(
Label::new(WidgetText::LayoutJob(layout_job.clone()))
Label::new(WidgetText::LayoutJob(Arc::from(layout_job.clone())))
.selectable(false),
)
})

View File

@@ -1,15 +1,17 @@
use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use color_eyre::eyre;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::WidgetText;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
use windows::Win32::Globalization::LCIDToLocaleName;
@@ -80,7 +82,7 @@ pub struct Keyboard {
/// - `Ok(String)`: The name of the active keyboard layout as a valid UTF-8 string.
/// - `Err(())`: Indicates that the function failed to retrieve the locale name or encountered
/// invalid UTF-16 characters during conversion.
fn get_active_keyboard_layout() -> Result<String, ()> {
fn get_active_keyboard_layout() -> eyre::Result<String, ()> {
let foreground_window_tid = unsafe { GetWindowThreadProcessId(GetForegroundWindow(), None) };
let lcid = unsafe { GetKeyboardLayout(foreground_window_tid) };
@@ -169,7 +171,10 @@ impl BarWidget for Keyboard {
);
config.apply_on_widget(true, ui, |ui| {
ui.add(Label::new(WidgetText::LayoutJob(layout_job.clone())).selectable(false))
ui.add(
Label::new(WidgetText::LayoutJob(Arc::from(layout_job.clone())))
.selectable(false),
)
});
}
}

View File

@@ -1,4 +1,6 @@
use super::ImageIcon;
use crate::MAX_LABEL_WIDTH;
use crate::MONITOR_INDEX;
use crate::config::DisplayFormat;
use crate::config::DisplayFormat::*;
use crate::config::WorkspacesDisplayFormat;
@@ -7,10 +9,6 @@ use crate::selected_frame::SelectableFrame;
use crate::ui::CustomUi;
use crate::widgets::komorebi_layout::KomorebiLayout;
use crate::widgets::widget::BarWidget;
use crate::MAX_LABEL_WIDTH;
use crate::MONITOR_INDEX;
use eframe::egui::text::LayoutJob;
use eframe::egui::vec2;
use eframe::egui::Align;
use eframe::egui::Color32;
use eframe::egui::Context;
@@ -28,6 +26,8 @@ use eframe::egui::StrokeKind;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::egui::text::LayoutJob;
use eframe::egui::vec2;
use komorebi_client::Container;
use komorebi_client::PathExt;
use komorebi_client::Rect;
@@ -271,14 +271,14 @@ impl Komorebi {
}
fn render_layout(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
if let Some(layout_config) = &self.layout {
if layout_config.enable {
let monitor_info = &mut *self.monitor_info.borrow_mut();
let workspace_idx = monitor_info.focused_workspace_idx;
monitor_info
.layout
.show(ctx, ui, config, layout_config, workspace_idx);
}
if let Some(layout_config) = &self.layout
&& layout_config.enable
{
let monitor_info = &mut *self.monitor_info.borrow_mut();
let workspace_idx = monitor_info.focused_workspace_idx;
monitor_info
.layout
.show(ctx, ui, config, layout_config, workspace_idx);
}
}
@@ -558,10 +558,8 @@ impl FocusedContainerBar {
ui.add(img.fit_to_exact_size(self.icon_size));
}
});
if HOVEL {
if let Some(title) = &info.title {
inner_response.response.on_hover_text(title);
}
if HOVEL && let Some(title) = &info.title {
inner_response.response.on_hover_text(title);
}
}
@@ -817,7 +815,7 @@ impl MonitorInfo {
self.mouse_follows_focus = state.mouse_follows_focus;
let monitor = &state.monitors.elements()[self.monitor_index];
self.work_area_offset = monitor.work_area_offset();
self.work_area_offset = monitor.work_area_offset;
self.focused_workspace_idx = Some(monitor.focused_workspace_idx());
// Layout
@@ -856,7 +854,7 @@ impl MonitorInfo {
let containers = fn_containers_from(ws);
WorkspaceInfo {
name: ws
.name()
.name
.to_owned()
.unwrap_or_else(|| format!("{}", index + 1)),
focused_container_idx: containers.iter().position(|c| c.is_focused),
@@ -864,7 +862,7 @@ impl MonitorInfo {
.iter()
.any(|container| container.windows.iter().any(|window| window.icon.is_some())),
containers,
layer: *ws.layer(),
layer: ws.layer,
should_show: !hide_empty_ws || focused_ws_idx == Some(index) || !ws.is_empty(),
is_selected: focused_ws_idx == Some(index),
}
@@ -873,15 +871,15 @@ impl MonitorInfo {
/// Determines the current layout of the focused workspace
fn resolve_layout(focused_ws: &Workspace, is_paused: bool) -> KomorebiLayout {
if focused_ws.monocle_container().is_some() {
if focused_ws.monocle_container.is_some() {
KomorebiLayout::Monocle
} else if !focused_ws.tile() {
} else if !focused_ws.tile {
KomorebiLayout::Floating
} else if is_paused {
KomorebiLayout::Paused
} else {
match focused_ws.layout() {
komorebi_client::Layout::Default(layout) => KomorebiLayout::Default(*layout),
match focused_ws.layout {
komorebi_client::Layout::Default(layout) => KomorebiLayout::Default(layout),
komorebi_client::Layout::Custom(_) => KomorebiLayout::Custom,
}
}
@@ -938,7 +936,7 @@ impl ContainerInfo {
// Monocle container first if present
let monocle = ws
.monocle_container()
.monocle_container
.as_ref()
.map(|c| Self::from_container(c, !has_focused_float));
@@ -965,7 +963,7 @@ impl ContainerInfo {
if let Some(window) = ws.floating_windows().iter().find(|w| w.is_focused()) {
return Some(Self::from_window(window));
}
if let Some(container) = ws.monocle_container() {
if let Some(container) = &ws.monocle_container {
Some(Self::from_container(container, true))
} else {
ws.focused_container()
@@ -979,7 +977,7 @@ impl ContainerInfo {
windows: container.windows().iter().map(WindowInfo::from).collect(),
focused_window_idx: container.focused_window_idx(),
is_focused,
is_locked: container.locked(),
is_locked: container.locked,
}
}

View File

@@ -2,7 +2,7 @@ use crate::config::DisplayFormat;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::komorebi::KomorebiLayoutConfig;
use eframe::egui::vec2;
use color_eyre::eyre;
use eframe::egui::Context;
use eframe::egui::CornerRadius;
use eframe::egui::FontId;
@@ -13,11 +13,12 @@ use eframe::egui::Stroke;
use eframe::egui::StrokeKind;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::egui::vec2;
use komorebi_client::SocketMessage;
use serde::de::Error;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::de::Error;
use serde_json::from_str;
use std::fmt::Display;
use std::fmt::Formatter;
@@ -34,7 +35,7 @@ pub enum KomorebiLayout {
}
impl<'de> Deserialize<'de> for KomorebiLayout {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
fn deserialize<D>(deserializer: D) -> eyre::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
@@ -91,16 +92,15 @@ impl KomorebiLayout {
fn on_click_option(&mut self, monitor_idx: usize, workspace_idx: Option<usize>) {
match self {
KomorebiLayout::Default(option) => {
if let Some(ws_idx) = workspace_idx {
if komorebi_client::send_message(&SocketMessage::WorkspaceLayout(
if let Some(ws_idx) = workspace_idx
&& komorebi_client::send_message(&SocketMessage::WorkspaceLayout(
monitor_idx,
ws_idx,
*option,
))
.is_err()
{
tracing::error!("could not send message to komorebi: WorkspaceLayout");
}
{
tracing::error!("could not send message to komorebi: WorkspaceLayout");
}
}
KomorebiLayout::Monocle => {
@@ -269,57 +269,53 @@ impl KomorebiLayout {
show_options = self.on_click(&show_options, monitor_idx, workspace_idx);
}
if show_options {
if let Some(workspace_idx) = workspace_idx {
Frame::NONE.show(ui, |ui| {
ui.add(
Label::new(egui_phosphor::regular::ARROW_FAT_LINES_RIGHT.to_string())
.selectable(false),
);
if show_options && let Some(workspace_idx) = workspace_idx {
Frame::NONE.show(ui, |ui| {
ui.add(
Label::new(egui_phosphor::regular::ARROW_FAT_LINES_RIGHT.to_string())
.selectable(false),
);
let mut layout_options = layout_config.options.clone().unwrap_or(vec![
KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Columns),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Rows),
KomorebiLayout::Default(komorebi_client::DefaultLayout::VerticalStack),
KomorebiLayout::Default(
komorebi_client::DefaultLayout::RightMainVerticalStack,
),
KomorebiLayout::Default(
komorebi_client::DefaultLayout::HorizontalStack,
),
KomorebiLayout::Default(
komorebi_client::DefaultLayout::UltrawideVerticalStack,
),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Grid),
//KomorebiLayout::Custom,
KomorebiLayout::Monocle,
KomorebiLayout::Floating,
KomorebiLayout::Paused,
]);
let mut layout_options = layout_config.options.clone().unwrap_or(vec![
KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Columns),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Rows),
KomorebiLayout::Default(komorebi_client::DefaultLayout::VerticalStack),
KomorebiLayout::Default(
komorebi_client::DefaultLayout::RightMainVerticalStack,
),
KomorebiLayout::Default(komorebi_client::DefaultLayout::HorizontalStack),
KomorebiLayout::Default(
komorebi_client::DefaultLayout::UltrawideVerticalStack,
),
KomorebiLayout::Default(komorebi_client::DefaultLayout::Grid),
//KomorebiLayout::Custom,
KomorebiLayout::Monocle,
KomorebiLayout::Floating,
KomorebiLayout::Paused,
]);
for layout_option in &mut layout_options {
let is_selected = self == layout_option;
for layout_option in &mut layout_options {
let is_selected = self == layout_option;
if SelectableFrame::new(is_selected)
.show(ui, |ui| {
layout_option.show_icon(is_selected, font_id.clone(), ctx, ui)
})
.on_hover_text(match layout_option {
KomorebiLayout::Default(layout) => layout.to_string(),
KomorebiLayout::Monocle => "Toggle monocle".to_string(),
KomorebiLayout::Floating => "Toggle tiling".to_string(),
KomorebiLayout::Paused => "Toggle pause".to_string(),
KomorebiLayout::Custom => "Custom".to_string(),
})
.clicked()
{
layout_option.on_click_option(monitor_idx, Some(workspace_idx));
show_options = false;
};
}
});
}
if SelectableFrame::new(is_selected)
.show(ui, |ui| {
layout_option.show_icon(is_selected, font_id.clone(), ctx, ui)
})
.on_hover_text(match layout_option {
KomorebiLayout::Default(layout) => layout.to_string(),
KomorebiLayout::Monocle => "Toggle monocle".to_string(),
KomorebiLayout::Floating => "Toggle tiling".to_string(),
KomorebiLayout::Paused => "Toggle pause".to_string(),
KomorebiLayout::Custom => "Custom".to_string(),
})
.clicked()
{
layout_option.on_click_option(monitor_idx, Some(workspace_idx));
show_options = false;
};
}
});
}
});

View File

@@ -1,15 +1,15 @@
use crate::MAX_LABEL_WIDTH;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::ui::CustomUi;
use crate::widgets::widget::BarWidget;
use crate::MAX_LABEL_WIDTH;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::sync::atomic::Ordering;
@@ -40,36 +40,34 @@ impl Media {
enable,
session_manager: GlobalSystemMediaTransportControlsSessionManager::RequestAsync()
.unwrap()
.get()
.join()
.unwrap(),
}
}
pub fn toggle(&self) {
if let Ok(session) = self.session_manager.GetCurrentSession() {
if let Ok(op) = session.TryTogglePlayPauseAsync() {
op.get().unwrap_or_default();
}
if let Ok(session) = self.session_manager.GetCurrentSession()
&& let Ok(op) = session.TryTogglePlayPauseAsync()
{
op.join().unwrap_or_default();
}
}
fn output(&mut self) -> String {
if let Ok(session) = self.session_manager.GetCurrentSession() {
if let Ok(operation) = session.TryGetMediaPropertiesAsync() {
if let Ok(properties) = operation.get() {
if let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title()) {
if artist.is_empty() {
return format!("{title}");
}
if title.is_empty() {
return format!("{artist}");
}
return format!("{artist} - {title}");
}
}
if let Ok(session) = self.session_manager.GetCurrentSession()
&& let Ok(operation) = session.TryGetMediaPropertiesAsync()
&& let Ok(properties) = operation.join()
&& let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title())
{
if artist.is_empty() {
return format!("{title}");
}
if title.is_empty() {
return format!("{artist}");
}
return format!("{artist} - {title}");
}
String::new()

View File

@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::process::Command;
@@ -124,12 +124,10 @@ impl BarWidget for Memory {
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
.clicked()
{
if let Err(error) =
&& let Err(error) =
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
{
eprintln!("{error}")
}
{
eprintln!("{error}")
}
});
}

View File

@@ -3,13 +3,13 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Color32;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use num_derive::FromPrimitive;
use serde::Deserialize;
use serde::Serialize;
@@ -58,6 +58,7 @@ pub struct NetworkSelectConfig {
impl From<NetworkConfig> for Network {
fn from(value: NetworkConfig) -> Self {
let default_refresh_interval = 10;
let data_refresh_interval = value.data_refresh_interval.unwrap_or(10);
Self {
@@ -67,10 +68,14 @@ impl From<NetworkConfig> for Network {
show_default_interface: value.show_default_interface.unwrap_or(true),
networks_network_activity: Networks::new_with_refreshed_list(),
default_interface: String::new(),
default_refresh_interval,
data_refresh_interval,
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
auto_select: value.auto_select,
activity_left_padding: value.activity_left_padding.unwrap_or_default(),
last_updated_default_interface: Instant::now()
.checked_sub(Duration::from_secs(default_refresh_interval))
.unwrap(),
last_state_total_activity: vec![],
last_state_activity: vec![],
last_updated_network_activity: Instant::now()
@@ -86,10 +91,12 @@ pub struct Network {
pub show_activity: bool,
pub show_default_interface: bool,
networks_network_activity: Networks,
default_refresh_interval: u64,
data_refresh_interval: u64,
label_prefix: LabelPrefix,
auto_select: Option<NetworkSelectConfig>,
default_interface: String,
last_updated_default_interface: Instant,
last_state_total_activity: Vec<NetworkReading>,
last_state_activity: Vec<NetworkReading>,
last_updated_network_activity: Instant,
@@ -98,10 +105,18 @@ pub struct Network {
impl Network {
fn default_interface(&mut self) {
if let Ok(interface) = netdev::get_default_interface() {
if let Some(friendly_name) = &interface.friendly_name {
let now = Instant::now();
if now.duration_since(self.last_updated_default_interface)
> Duration::from_secs(self.default_refresh_interval)
{
if let Ok(interface) = netdev::get_default_interface()
&& let Some(friendly_name) = &interface.friendly_name
{
self.default_interface.clone_from(friendly_name);
}
self.last_updated_default_interface = now;
}
}
@@ -116,43 +131,40 @@ impl Network {
activity.clear();
total_activity.clear();
if let Ok(interface) = netdev::get_default_interface() {
if let Some(friendly_name) = &interface.friendly_name {
self.default_interface.clone_from(friendly_name);
if let Ok(interface) = netdev::get_default_interface()
&& let Some(friendly_name) = &interface.friendly_name
{
self.default_interface.clone_from(friendly_name);
self.networks_network_activity.refresh(true);
self.networks_network_activity.refresh(true);
for (interface_name, data) in &self.networks_network_activity {
if friendly_name.eq(interface_name) {
if self.show_activity {
let received = Self::to_pretty_bytes(
data.received(),
self.data_refresh_interval,
);
let transmitted = Self::to_pretty_bytes(
data.transmitted(),
self.data_refresh_interval,
);
for (interface_name, data) in &self.networks_network_activity {
if friendly_name.eq(interface_name) {
if self.show_activity {
let received =
Self::to_pretty_bytes(data.received(), self.data_refresh_interval);
let transmitted = Self::to_pretty_bytes(
data.transmitted(),
self.data_refresh_interval,
);
activity.push(NetworkReading::new(
NetworkReadingFormat::Speed,
ReadingValue::from(received),
ReadingValue::from(transmitted),
));
}
activity.push(NetworkReading::new(
NetworkReadingFormat::Speed,
ReadingValue::from(received),
ReadingValue::from(transmitted),
));
}
if self.show_total_activity {
let total_received =
Self::to_pretty_bytes(data.total_received(), 1);
let total_transmitted =
Self::to_pretty_bytes(data.total_transmitted(), 1);
if self.show_total_activity {
let total_received = Self::to_pretty_bytes(data.total_received(), 1);
let total_transmitted =
Self::to_pretty_bytes(data.total_transmitted(), 1);
total_activity.push(NetworkReading::new(
NetworkReadingFormat::Total,
ReadingValue::from(total_received),
ReadingValue::from(total_transmitted),
))
}
total_activity.push(NetworkReading::new(
NetworkReadingFormat::Total,
ReadingValue::from(total_received),
ReadingValue::from(total_transmitted),
))
}
}
}
@@ -312,10 +324,9 @@ impl Network {
if SelectableFrame::new_auto(selected, auto_focus_fill)
.show(ui, add_contents)
.clicked()
&& let Err(error) = Command::new("cmd.exe").args(["/C", "ncpa"]).spawn()
{
if let Err(error) = Command::new("cmd.exe").args(["/C", "ncpa"]).spawn() {
eprintln!("{error}");
}
eprintln!("{error}");
}
}
}

View File

@@ -3,12 +3,12 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::process::Command;
@@ -156,17 +156,15 @@ impl BarWidget for Storage {
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
.clicked()
{
if let Err(error) = Command::new("cmd.exe")
&& let Err(error) = Command::new("cmd.exe")
.args([
"/C",
"explorer.exe",
output.label.split(' ').collect::<Vec<&str>>()[0],
])
.spawn()
{
eprintln!("{error}")
}
{
eprintln!("{error}")
}
});
}

View File

@@ -6,7 +6,6 @@ use crate::widgets::widget::BarWidget;
use chrono::Local;
use chrono::NaiveTime;
use chrono_tz::Tz;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::CornerRadius;
@@ -16,6 +15,7 @@ use eframe::egui::Stroke;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::Vec2;
use eframe::egui::text::LayoutJob;
use eframe::epaint::StrokeKind;
use lazy_static::lazy_static;
use serde::Deserialize;

View File

@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
use crate::render::RenderConfig;
use crate::selected_frame::SelectableFrame;
use crate::widgets::widget::BarWidget;
use eframe::egui::text::LayoutJob;
use eframe::egui::Align;
use eframe::egui::Context;
use eframe::egui::Label;
use eframe::egui::TextFormat;
use eframe::egui::Ui;
use eframe::egui::text::LayoutJob;
use serde::Deserialize;
use serde::Serialize;
use std::process::Command;
@@ -140,16 +140,14 @@ impl BarWidget for Update {
if SelectableFrame::new(false)
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
.clicked()
{
if let Err(error) = Command::new("explorer.exe")
&& let Err(error) = Command::new("explorer.exe")
.args([format!(
"https://github.com/LGUG2Z/komorebi/releases/v{}",
self.latest_version
)])
.spawn()
{
eprintln!("{error}")
}
{
eprintln!("{error}")
}
});
}

View File

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

View File

@@ -1,8 +1,31 @@
#![warn(clippy::all)]
#![allow(clippy::missing_errors_doc)]
pub use komorebi::animation::prefix::AnimationPrefix;
pub use komorebi::AnimationsConfig;
pub use komorebi::AppSpecificConfigurationPath;
pub use komorebi::AspectRatio;
pub use komorebi::BorderColours;
pub use komorebi::Colour;
pub use komorebi::CrossBoundaryBehaviour;
pub use komorebi::GlobalState;
pub use komorebi::KomorebiTheme;
pub use komorebi::MonitorConfig;
pub use komorebi::Notification;
pub use komorebi::NotificationEvent;
pub use komorebi::PredefinedAspectRatio;
pub use komorebi::Rgb;
pub use komorebi::RuleDebug;
pub use komorebi::StackbarConfig;
pub use komorebi::State;
pub use komorebi::StaticConfig;
pub use komorebi::SubscribeOptions;
pub use komorebi::TabsConfig;
pub use komorebi::VirtualDesktopNotification;
pub use komorebi::WindowContainerBehaviour;
pub use komorebi::WindowsApi;
pub use komorebi::WorkspaceConfig;
pub use komorebi::animation::PerAnimationPrefixConfig;
pub use komorebi::animation::prefix::AnimationPrefix;
pub use komorebi::asc::ApplicationSpecificConfiguration;
pub use komorebi::border_manager::BorderInfo;
pub use komorebi::config_generation::ApplicationConfiguration;
@@ -11,8 +34,6 @@ pub use komorebi::config_generation::IdWithIdentifierAndComment;
pub use komorebi::config_generation::MatchingRule;
pub use komorebi::config_generation::MatchingStrategy;
pub use komorebi::container::Container;
pub use komorebi::core::config_generation::ApplicationConfigurationGenerator;
pub use komorebi::core::replace_env_in_path;
pub use komorebi::core::AnimationStyle;
pub use komorebi::core::ApplicationIdentifier;
pub use komorebi::core::Arrangement;
@@ -42,6 +63,8 @@ pub use komorebi::core::StackbarLabel;
pub use komorebi::core::StackbarMode;
pub use komorebi::core::StateQuery;
pub use komorebi::core::WindowKind;
pub use komorebi::core::config_generation::ApplicationConfigurationGenerator;
pub use komorebi::core::replace_env_in_path;
pub use komorebi::monitor::Monitor;
pub use komorebi::monitor_reconciliator::MonitorNotification;
pub use komorebi::ring::Ring;
@@ -51,29 +74,6 @@ pub use komorebi::window_manager_event::WindowManagerEvent;
pub use komorebi::workspace::Workspace;
pub use komorebi::workspace::WorkspaceGlobals;
pub use komorebi::workspace::WorkspaceLayer;
pub use komorebi::AnimationsConfig;
pub use komorebi::AppSpecificConfigurationPath;
pub use komorebi::AspectRatio;
pub use komorebi::BorderColours;
pub use komorebi::Colour;
pub use komorebi::CrossBoundaryBehaviour;
pub use komorebi::GlobalState;
pub use komorebi::KomorebiTheme;
pub use komorebi::MonitorConfig;
pub use komorebi::Notification;
pub use komorebi::NotificationEvent;
pub use komorebi::PredefinedAspectRatio;
pub use komorebi::Rgb;
pub use komorebi::RuleDebug;
pub use komorebi::StackbarConfig;
pub use komorebi::State;
pub use komorebi::StaticConfig;
pub use komorebi::SubscribeOptions;
pub use komorebi::TabsConfig;
pub use komorebi::VirtualDesktopNotification;
pub use komorebi::WindowContainerBehaviour;
pub use komorebi::WindowsApi;
pub use komorebi::WorkspaceConfig;
use komorebi::DATA_DIR;

View File

@@ -1,7 +1,7 @@
[package]
name = "komorebi-gui"
version = "0.1.38"
edition = "2021"
version = "0.1.39"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,9 +1,9 @@
#![warn(clippy::all)]
use eframe::egui;
use eframe::egui::color_picker::Alpha;
use eframe::egui::Color32;
use eframe::egui::ViewportBuilder;
use eframe::egui::color_picker::Alpha;
use komorebi_client::BorderStyle;
use komorebi_client::Colour;
use komorebi_client::DefaultLayout;
@@ -78,8 +78,8 @@ impl From<&komorebi_client::Monitor> for MonitorConfig {
}
Self {
size: *value.size(),
work_area_offset: value.work_area_offset().unwrap_or_default(),
size: value.size,
work_area_offset: value.work_area_offset.unwrap_or_default(),
workspaces,
}
}
@@ -95,22 +95,22 @@ struct WorkspaceConfig {
impl From<&komorebi_client::Workspace> for WorkspaceConfig {
fn from(value: &komorebi_client::Workspace) -> Self {
let layout = match value.layout() {
Layout::Default(layout) => *layout,
let layout = match value.layout {
Layout::Default(layout) => layout,
Layout::Custom(_) => DefaultLayout::BSP,
};
let name = value
.name()
.name
.to_owned()
.unwrap_or_else(|| random_word::get(random_word::Lang::En).to_string());
Self {
layout,
name,
tile: *value.tile(),
workspace_padding: value.workspace_padding().unwrap_or(20),
container_padding: value.container_padding().unwrap_or(20),
tile: value.tile,
workspace_padding: value.workspace_padding.unwrap_or(20),
container_padding: value.container_padding.unwrap_or(20),
}
}
}
@@ -437,7 +437,7 @@ impl eframe::App for KomorebiGui {
BorderStyle::Square,
] {
if ui
.add(egui::SelectableLabel::new(
.add(egui::Button::selectable(
self.border_config.border_style == option,
option.to_string(),
))
@@ -494,7 +494,7 @@ impl eframe::App for KomorebiGui {
StackbarMode::Always,
] {
if ui
.add(egui::SelectableLabel::new(
.add(egui::Button::selectable(
self.stackbar_config.mode == option,
option.to_string(),
))
@@ -513,7 +513,7 @@ impl eframe::App for KomorebiGui {
ui.collapsing("Label", |ui| {
for option in [StackbarLabel::Process, StackbarLabel::Title] {
if ui
.add(egui::SelectableLabel::new(
.add(egui::Button::selectable(
self.stackbar_config.label == option,
option.to_string(),
))
@@ -772,7 +772,7 @@ impl eframe::App for KomorebiGui {
DefaultLayout::Grid,
] {
if ui
.add(egui::SelectableLabel::new(
.add(egui::Button::selectable(
workspace.layout == option,
option.to_string(),
))

View File

@@ -1,12 +1,11 @@
[package]
name = "komorebi-themes"
version = "0.1.38"
edition = "2021"
version = "0.1.39"
edition = "2024"
[dependencies]
base16-egui-themes = { git = "https://github.com/LGUG2Z/base16-egui-themes", rev = "96f26c88d83781f234d42222293ec73d23a39ad8" }
catppuccin-egui = { git = "https://github.com/LGUG2Z/catppuccin-egui", rev = "bdaff30959512c4f7ee7304117076a48633d777f", default-features = false, features = ["egui31"] }
#catppuccin-egui = { version = "5", default-features = false, features = ["egui30"] }
base16-egui-themes = { git = "https://github.com/LGUG2Z/base16-egui-themes", rev = "c9008bd5cfa288c926e9ea3aa18c92073f9281bd" }
catppuccin-egui = { version = "5", default-features = false, features = ["egui32"] }
eframe = { workspace = true }
schemars = { workspace = true, optional = true }
serde = { workspace = true }

View File

@@ -1,6 +1,6 @@
use hex_color::HexColor;
#[cfg(feature = "schemars")]
use schemars::gen::SchemaGenerator;
use schemars::SchemaGenerator;
#[cfg(feature = "schemars")]
use schemars::schema::InstanceType;
#[cfg(feature = "schemars")]

View File

@@ -1,6 +1,6 @@
use crate::Base16ColourPalette;
use crate::colour::Colour;
use crate::colour::Hex;
use crate::Base16ColourPalette;
use hex_color::HexColor;
use std::collections::VecDeque;
use std::fmt::Display;

View File

@@ -4,8 +4,8 @@
pub mod colour;
mod generator;
pub use generator::generate_base16_palette;
pub use generator::ThemeVariant;
pub use generator::generate_base16_palette;
use schemars::JsonSchema;
use serde::Deserialize;
@@ -16,14 +16,14 @@ use strum::IntoEnumIterator;
use crate::colour::Colour;
pub use base16_egui_themes::Base16;
pub use catppuccin_egui;
use eframe::egui::style::Selection;
use eframe::egui::style::WidgetVisuals;
use eframe::egui::style::Widgets;
pub use eframe::egui::Color32;
use eframe::egui::Shadow;
use eframe::egui::Stroke;
use eframe::egui::Style;
use eframe::egui::Visuals;
use eframe::egui::style::Selection;
use eframe::egui::style::WidgetVisuals;
use eframe::egui::style::Widgets;
use serde_variant::to_variant_name;
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]

View File

@@ -1,9 +1,9 @@
[package]
name = "komorebi"
version = "0.1.38"
version = "0.1.39"
description = "A tiling window manager for Windows"
repository = "https://github.com/LGUG2Z/komorebi"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -17,7 +17,6 @@ crossbeam-channel = { workspace = true }
crossbeam-utils = { workspace = true }
ctrlc = { version = "3", features = ["termination"] }
dirs = { workspace = true }
getset = "0.1"
hotwatch = { workspace = true }
lazy_static = { workspace = true }
miow = "0.6"

View File

@@ -1,5 +1,5 @@
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use super::prefix::AnimationPrefix;

View File

@@ -1,4 +1,4 @@
use color_eyre::Result;
use color_eyre::eyre;
use serde::Deserialize;
use serde::Serialize;
@@ -6,10 +6,10 @@ use std::sync::atomic::Ordering;
use std::time::Duration;
use std::time::Instant;
use super::RenderDispatcher;
use super::ANIMATION_DURATION_GLOBAL;
use super::ANIMATION_FPS;
use super::ANIMATION_MANAGER;
use super::RenderDispatcher;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
@@ -55,9 +55,9 @@ impl AnimationEngine {
#[allow(clippy::cast_precision_loss)]
pub fn animate(
render_dispatcher: (impl RenderDispatcher + Send + 'static),
render_dispatcher: impl RenderDispatcher + Send + 'static,
duration: Duration,
) -> Result<()> {
) -> eyre::Result<()> {
std::thread::spawn(move || {
let animation_key = render_dispatcher.get_animation_key();
if ANIMATION_MANAGER.lock().in_progress(animation_key.as_str()) {

View File

@@ -1,5 +1,5 @@
use crate::core::Rect;
use crate::AnimationStyle;
use crate::core::Rect;
use super::style::apply_ease_func;

View File

@@ -4,9 +4,9 @@ use crate::core::animation::AnimationStyle;
use lazy_static::lazy_static;
use prefix::AnimationPrefix;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU64;
use std::sync::Arc;
use parking_lot::Mutex;

View File

@@ -1,8 +1,8 @@
use color_eyre::Result;
use color_eyre::eyre;
pub trait RenderDispatcher {
fn get_animation_key(&self) -> String;
fn pre_render(&self) -> Result<()>;
fn render(&self, delta: f64) -> Result<()>;
fn post_render(&self) -> Result<()>;
fn pre_render(&self) -> eyre::Result<()>;
fn render(&self, delta: f64) -> eyre::Result<()>;
fn post_render(&self) -> eyre::Result<()>;
}

View File

@@ -1,33 +1,30 @@
use crate::border_manager::window_kind_colour;
use crate::border_manager::RenderTarget;
use crate::border_manager::WindowKind;
use crate::WINDOWS_11;
use crate::WindowsApi;
use crate::border_manager::BORDER_OFFSET;
use crate::border_manager::BORDER_WIDTH;
use crate::border_manager::RenderTarget;
use crate::border_manager::STYLE;
use crate::border_manager::WindowKind;
use crate::border_manager::window_kind_colour;
use crate::core::BorderStyle;
use crate::core::Rect;
use crate::windows_api;
use crate::WindowsApi;
use crate::WINDOWS_11;
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::LazyLock;
use std::sync::atomic::Ordering;
use std::sync::mpsc;
use std::sync::LazyLock;
use windows::Win32::Foundation::FALSE;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::TRUE;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Direct2D::Common::D2D_RECT_F;
use windows::Win32::Graphics::Direct2D::Common::D2D_SIZE_U;
use windows::Win32::Graphics::Direct2D::Common::D2D1_ALPHA_MODE_PREMULTIPLIED;
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
use windows::Win32::Graphics::Direct2D::Common::D2D1_PIXEL_FORMAT;
use windows::Win32::Graphics::Direct2D::Common::D2D_RECT_F;
use windows::Win32::Graphics::Direct2D::Common::D2D_SIZE_U;
use windows::Win32::Graphics::Direct2D::D2D1CreateFactory;
use windows::Win32::Graphics::Direct2D::ID2D1Factory;
use windows::Win32::Graphics::Direct2D::ID2D1SolidColorBrush;
use windows::Win32::Graphics::Direct2D::D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
use windows::Win32::Graphics::Direct2D::D2D1_BRUSH_PROPERTIES;
use windows::Win32::Graphics::Direct2D::D2D1_FACTORY_TYPE_MULTI_THREADED;
@@ -36,31 +33,34 @@ use windows::Win32::Graphics::Direct2D::D2D1_PRESENT_OPTIONS_IMMEDIATELY;
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_PROPERTIES;
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_TYPE_DEFAULT;
use windows::Win32::Graphics::Direct2D::D2D1_ROUNDED_RECT;
use windows::Win32::Graphics::Dwm::DwmEnableBlurBehindWindow;
use windows::Win32::Graphics::Direct2D::D2D1CreateFactory;
use windows::Win32::Graphics::Direct2D::ID2D1Factory;
use windows::Win32::Graphics::Direct2D::ID2D1SolidColorBrush;
use windows::Win32::Graphics::Dwm::DWM_BB_BLURREGION;
use windows::Win32::Graphics::Dwm::DWM_BB_ENABLE;
use windows::Win32::Graphics::Dwm::DWM_BLURBEHIND;
use windows::Win32::Graphics::Dwm::DwmEnableBlurBehindWindow;
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_UNKNOWN;
use windows::Win32::Graphics::Gdi::CreateRectRgn;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::UI::WindowsAndMessaging::CREATESTRUCTW;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::LoadCursorW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::SetCursor;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::CREATESTRUCTW;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
use windows::Win32::UI::WindowsAndMessaging::GWLP_USERDATA;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::IDC_ARROW;
use windows::Win32::UI::WindowsAndMessaging::LoadCursorW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::SM_CXVIRTUALSCREEN;
use windows::Win32::UI::WindowsAndMessaging::SetCursor;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::WM_CREATE;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
@@ -102,10 +102,10 @@ pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL {
let hwnds = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
let hwnd = hwnd.0 as isize;
if let Ok(class) = WindowsApi::real_window_class_w(hwnd) {
if class.starts_with("komoborder") {
hwnds.push(hwnd);
}
if let Ok(class) = WindowsApi::real_window_class_w(hwnd)
&& class.starts_with("komoborder")
{
hwnds.push(hwnd);
}
true.into()
@@ -392,63 +392,63 @@ impl Border {
tracing::error!("failed to update border position {error}");
}
if !rect.is_same_size_as(&old_rect) || !rect.has_same_position_as(&old_rect) {
if let Some(render_target) = (*border_pointer).render_target.as_ref() {
let border_width = (*border_pointer).width;
let border_offset = (*border_pointer).offset;
if (!rect.is_same_size_as(&old_rect) || !rect.has_same_position_as(&old_rect))
&& let Some(render_target) = (*border_pointer).render_target.as_ref()
{
let border_width = (*border_pointer).width;
let border_offset = (*border_pointer).offset;
(*border_pointer).rounded_rect.rect = D2D_RECT_F {
left: (border_width / 2 - border_offset) as f32,
top: (border_width / 2 - border_offset) as f32,
right: (rect.right - border_width / 2 + border_offset) as f32,
bottom: (rect.bottom - border_width / 2 + border_offset) as f32,
(*border_pointer).rounded_rect.rect = D2D_RECT_F {
left: (border_width / 2 - border_offset) as f32,
top: (border_width / 2 - border_offset) as f32,
right: (rect.right - border_width / 2 + border_offset) as f32,
bottom: (rect.bottom - border_width / 2 + border_offset) as f32,
};
let _ = render_target.Resize(&D2D_SIZE_U {
width: rect.right as u32,
height: rect.bottom as u32,
});
let window_kind = (*border_pointer).window_kind;
if let Some(brush) = (*border_pointer).brushes.get(&window_kind) {
render_target.BeginDraw();
render_target.Clear(None);
// Calculate border radius based on style
let style = match (*border_pointer).style {
BorderStyle::System => {
if *WINDOWS_11 {
BorderStyle::Rounded
} else {
BorderStyle::Square
}
}
BorderStyle::Rounded => BorderStyle::Rounded,
BorderStyle::Square => BorderStyle::Square,
};
let _ = render_target.Resize(&D2D_SIZE_U {
width: rect.right as u32,
height: rect.bottom as u32,
});
let window_kind = (*border_pointer).window_kind;
if let Some(brush) = (*border_pointer).brushes.get(&window_kind) {
render_target.BeginDraw();
render_target.Clear(None);
// Calculate border radius based on style
let style = match (*border_pointer).style {
BorderStyle::System => {
if *WINDOWS_11 {
BorderStyle::Rounded
} else {
BorderStyle::Square
}
}
BorderStyle::Rounded => BorderStyle::Rounded,
BorderStyle::Square => BorderStyle::Square,
};
match style {
BorderStyle::Rounded => {
render_target.DrawRoundedRectangle(
&(*border_pointer).rounded_rect,
brush,
border_width as f32,
None,
);
}
BorderStyle::Square => {
render_target.DrawRectangle(
&(*border_pointer).rounded_rect.rect,
brush,
border_width as f32,
None,
);
}
_ => {}
match style {
BorderStyle::Rounded => {
render_target.DrawRoundedRectangle(
&(*border_pointer).rounded_rect,
brush,
border_width as f32,
None,
);
}
let _ = render_target.EndDraw(None, None);
BorderStyle::Square => {
render_target.DrawRectangle(
&(*border_pointer).rounded_rect.rect,
brush,
border_width as f32,
None,
);
}
_ => {}
}
let _ = render_target.EndDraw(None, None);
}
}

View File

@@ -1,6 +1,8 @@
#![deny(clippy::unwrap_used, clippy::expect_used)]
mod border;
use crate::WindowManager;
use crate::WindowsApi;
use crate::core::BorderImplementation;
use crate::core::BorderStyle;
use crate::core::WindowKind;
@@ -8,10 +10,8 @@ use crate::ring::Ring;
use crate::windows_api;
use crate::workspace::Workspace;
use crate::workspace::WorkspaceLayer;
use crate::WindowManager;
use crate::WindowsApi;
use border::border_hwnds;
pub use border::Border;
use border::border_hwnds;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicCell;
@@ -22,15 +22,15 @@ use lazy_static::lazy_static;
use parking_lot::Mutex;
use serde::Deserialize;
use serde::Serialize;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::OnceLock;
use strum::Display;
use windows::Win32::Foundation::HWND;
use windows::Win32::Graphics::Direct2D::ID2D1HwndRenderTarget;
@@ -170,13 +170,15 @@ fn window_kind_colour(focus_kind: WindowKind) -> u32 {
}
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
std::thread::spawn(move || loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});
@@ -209,9 +211,9 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
.iter()
.map(|w| w.hwnd)
.collect::<Vec<_>>();
let workspace_layer = *state.monitors.elements()[focused_monitor_idx].workspaces()
let workspace_layer = state.monitors.elements()[focused_monitor_idx].workspaces()
[focused_workspace_idx]
.layer();
.layer;
let foreground_window = WindowsApi::foreground_window().unwrap_or_default();
let layer_changed = previous_layer != workspace_layer;
let forced_update = matches!(notification, Notification::ForceUpdate);
@@ -224,7 +226,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
// Only operate on the focused workspace of each monitor
if let Some(ws) = m.focused_workspace() {
// Handle the monocle container separately
if let Some(monocle) = ws.monocle_container() {
if let Some(monocle) = &ws.monocle_container {
let window_kind = if monitor_idx != focused_monitor_idx {
WindowKind::Unfocused
} else {
@@ -237,7 +239,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
.unwrap_or_default()
.set_accent(window_kind_colour(window_kind))?;
if ws.layer() == &WorkspaceLayer::Floating {
if ws.layer == WorkspaceLayer::Floating {
for window in ws.floating_windows() {
let mut window_kind = WindowKind::Unfocused;
@@ -255,7 +257,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let window_kind = if idx != ws.focused_container_idx()
|| monitor_idx != focused_monitor_idx
{
if c.locked() {
if c.locked {
WindowKind::UnfocusedLocked
} else {
WindowKind::Unfocused
@@ -339,15 +341,11 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
should_process_notification = true;
}
if !should_process_notification {
if let Some(Notification::Update(ref previous)) = previous_notification
{
if previous.unwrap_or_default()
!= notification_hwnd.unwrap_or_default()
{
should_process_notification = true;
}
}
if !should_process_notification
&& let Some(Notification::Update(ref previous)) = previous_notification
&& previous.unwrap_or_default() != notification_hwnd.unwrap_or_default()
{
should_process_notification = true;
}
should_process_notification
@@ -383,7 +381,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
// Only operate on the focused workspace of each monitor
if let Some(ws) = m.focused_workspace() {
// Workspaces with tiling disabled don't have borders
if !ws.tile() {
if !ws.tile {
// Remove all borders on this monitor
remove_borders(
&mut borders,
@@ -396,16 +394,16 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
}
// Handle the monocle container separately
if let Some(monocle) = ws.monocle_container() {
if let Some(monocle) = &ws.monocle_container {
let mut new_border = false;
let focused_window_hwnd =
monocle.focused_window().map(|w| w.hwnd).unwrap_or_default();
let id = monocle.id().clone();
let id = monocle.id.clone();
let border = match borders.entry(id.clone()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
if let Ok(border) = Border::create(
monocle.id(),
&monocle.id,
focused_window_hwnd,
monitor_idx,
) {
@@ -463,7 +461,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let border_hwnd = border.hwnd;
if ws.layer() == &WorkspaceLayer::Floating {
if ws.layer == WorkspaceLayer::Floating {
handle_floating_borders(
&mut borders,
&mut windows_borders,
@@ -502,8 +500,8 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let foreground_hwnd = WindowsApi::foreground_window().unwrap_or_default();
let foreground_monitor_id =
WindowsApi::monitor_from_window(foreground_hwnd);
let is_maximized = foreground_monitor_id == m.id()
&& WindowsApi::is_zoomed(foreground_hwnd);
let is_maximized =
foreground_monitor_id == m.id && WindowsApi::is_zoomed(foreground_hwnd);
if is_maximized {
// Remove all borders on this monitor
@@ -521,7 +519,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let mut container_and_floating_window_ids = ws
.containers()
.iter()
.map(|c| c.id().clone())
.map(|c| c.id.clone())
.collect::<Vec<_>>();
for w in ws.floating_windows() {
@@ -539,7 +537,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
'containers: for (idx, c) in ws.containers().iter().enumerate() {
let focused_window_hwnd =
c.focused_window().map(|w| w.hwnd).unwrap_or_default();
let id = c.id().clone();
let id = c.id.clone();
// Get the border entry for this container from the map or create one
let mut new_border = false;
@@ -547,7 +545,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
if let Ok(border) =
Border::create(c.id(), focused_window_hwnd, monitor_idx)
Border::create(&c.id, focused_window_hwnd, monitor_idx)
{
new_border = true;
entry.insert(border)
@@ -563,7 +561,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|| monitor_idx != focused_monitor_idx
|| focused_window_hwnd != foreground_window
{
if c.locked() {
if c.locked {
WindowKind::UnfocusedLocked
} else {
WindowKind::Unfocused
@@ -603,7 +601,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let rect = match WindowsApi::window_rect(focused_window_hwnd) {
Ok(rect) => rect,
Err(_) => {
remove_border(c.id(), &mut borders, &mut windows_borders)?;
remove_border(&c.id, &mut borders, &mut windows_borders)?;
continue 'containers;
}
};

View File

@@ -6,17 +6,17 @@
use std::ffi::c_void;
use std::ops::Deref;
use windows::core::IUnknown;
use windows::core::IUnknown_Vtbl;
use windows::core::GUID;
use windows::core::HRESULT;
use windows::core::HSTRING;
use windows::core::PCWSTR;
use windows::core::PWSTR;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::SIZE;
use windows::Win32::UI::Shell::Common::IObjectArray;
use windows::core::GUID;
use windows::core::HRESULT;
use windows::core::HSTRING;
use windows::core::IUnknown;
use windows::core::IUnknown_Vtbl;
use windows::core::PCWSTR;
use windows::core::PWSTR;
use windows_core::BOOL;
type DesktopID = GUID;
@@ -129,7 +129,7 @@ pub unsafe trait IApplicationView: IUnknown {
pub unsafe fn get_app_user_model_id(&self, id: *mut PWSTR) -> HRESULT; // Proc17
pub unsafe fn set_app_user_model_id(&self, id: PCWSTR) -> HRESULT;
pub unsafe fn is_equal_by_app_user_model_id(&self, id: PCWSTR, out_result: *mut INT)
-> HRESULT;
-> HRESULT;
/*** IApplicationView methods ***/
pub unsafe fn get_view_state(&self, out_state: *mut UINT) -> HRESULT; // Proc20

View File

@@ -11,11 +11,11 @@ use interfaces::IServiceProvider;
use std::ffi::c_void;
use windows::Win32::Foundation::HWND;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::System::Com::COINIT_MULTITHREADED;
use windows::Win32::System::Com::CoCreateInstance;
use windows::Win32::System::Com::CoInitializeEx;
use windows::Win32::System::Com::CoUninitialize;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::System::Com::COINIT_MULTITHREADED;
use windows_core::Interface;
struct ComInit();
@@ -64,7 +64,7 @@ fn get_iapplication_view_collection(provider: &IServiceProvider) -> IApplication
})
}
#[no_mangle]
#[unsafe(no_mangle)]
pub extern "C" fn SetCloak(hwnd: HWND, cloak_type: u32, flags: i32) {
COM_INIT.with(|_| {
let provider = get_iservice_provider();

View File

@@ -1,24 +1,19 @@
use std::collections::VecDeque;
use getset::CopyGetters;
use getset::Getters;
use getset::Setters;
use nanoid::nanoid;
use serde::Deserialize;
use serde::Serialize;
use crate::Lockable;
use crate::ring::Ring;
use crate::window::Window;
use crate::Lockable;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters, CopyGetters, Setters)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Container {
#[getset(get = "pub")]
id: String,
pub id: String,
#[serde(default)]
#[getset(get_copy = "pub", set = "pub")]
locked: bool,
pub locked: bool,
windows: Ring<Window>,
}
@@ -50,12 +45,11 @@ impl Container {
for window in self.windows().iter().rev() {
let mut should_hide = omit.is_none();
if !should_hide {
if let Some(omit) = omit {
if omit != window.hwnd {
should_hide = true
}
}
if !should_hide
&& let Some(omit) = omit
&& omit != window.hwnd
{
should_hide = true
}
if should_hide {
@@ -87,10 +81,10 @@ impl Container {
pub fn hwnd_from_exe(&self, exe: &str) -> Option<isize> {
for window in self.windows() {
if let Ok(window_exe) = window.exe() {
if exe == window_exe {
return Option::from(window.hwnd);
}
if let Ok(window_exe) = window.exe()
&& exe == window_exe
{
return Option::from(window.hwnd);
}
}
@@ -99,10 +93,10 @@ impl Container {
pub fn idx_from_exe(&self, exe: &str) -> Option<usize> {
for (idx, window) in self.windows().iter().enumerate() {
if let Ok(window_exe) = window.exe() {
if exe == window_exe {
return Option::from(idx);
}
if let Ok(window_exe) = window.exe()
&& exe == window_exe
{
return Option::from(idx);
}
}
@@ -278,8 +272,8 @@ mod tests {
}"#;
let container: Container = serde_json::from_str(json).expect("Should deserialize");
assert!(!container.locked());
assert_eq!(container.id(), "test-1");
assert!(!container.locked);
assert_eq!(container.id, "test-1");
assert!(container.windows().is_empty());
let json = r#"{
@@ -287,8 +281,8 @@ mod tests {
"windows": { "elements": [ { "hwnd": 5 }, { "hwnd": 9 } ], "focused": 1 }
}"#;
let container: Container = serde_json::from_str(json).unwrap();
assert_eq!(container.id(), "test-2");
assert!(!container.locked());
assert_eq!(container.id, "test-2");
assert!(!container.locked);
assert_eq!(container.windows(), &[Window::from(5), Window::from(9)]);
assert_eq!(container.focused_window_idx(), 1);
}
@@ -302,7 +296,7 @@ mod tests {
let deserialized: Container =
serde_json::from_str(&serialized).expect("Should deserialize");
assert!(deserialized.locked());
assert_eq!(deserialized.id(), container.id());
assert!(deserialized.locked);
assert_eq!(deserialized.id, container.id);
}
}

View File

@@ -1,8 +1,8 @@
use clap::ValueEnum;
use serde::ser::SerializeSeq;
use serde::Deserialize;
use serde::Serialize;
use serde::ser::SerializeSeq;
use strum::Display;
use strum::EnumString;

View File

@@ -6,12 +6,12 @@ use serde::Serialize;
use strum::Display;
use strum::EnumString;
use super::custom_layout::Column;
use super::custom_layout::ColumnSplit;
use super::custom_layout::ColumnSplitWithCapacity;
use super::CustomLayout;
use super::DefaultLayout;
use super::Rect;
use super::custom_layout::Column;
use super::custom_layout::ColumnSplit;
use super::custom_layout::ColumnSplitWithCapacity;
use crate::default_layout::LayoutOptions;
pub trait Arrangement {
@@ -52,94 +52,83 @@ impl Arrangement for DefaultLayout {
let column_width = area.right / column_count as i32;
let mut layouts = Vec::with_capacity(len);
match len {
// treat < 3 windows the same as the columns layout
len if len < 3 => {
layouts = columns(area, len);
let visible_columns = area.right / column_width;
let keep_centered = layout_options
.and_then(|o| {
o.scrolling
.map(|s| s.center_focused_column.unwrap_or_default())
})
.unwrap_or(false);
let adjustment = calculate_columns_adjustment(resize_dimensions);
layouts.iter_mut().zip(adjustment.iter()).for_each(
|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
},
);
let first_visible: isize = if focused_idx == 0 {
// if focused idx is 0, we are at the beginning of the scrolling strip
0
} else {
let previous_first_visible = if latest_layout.is_empty() {
0
} else {
// previous first_visible based on the left position of the first visible window
let left_edge = area.left;
latest_layout
.iter()
.position(|rect| rect.left >= left_edge)
.unwrap_or(0) as isize
};
if matches!(
layout_flip,
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
columns_reverse(&mut layouts);
}
}
}
// treat >= column_count as scrolling
len => {
let visible_columns = area.right / column_width;
let first_visible: isize = if focused_idx == 0 {
// if focused idx is 0, we are at the beginning of the scrolling strip
0
let focused_idx = focused_idx as isize;
// if center_focused_column is enabled, and we have an odd number of visible columns,
// center the focused window column
if keep_centered && visible_columns % 2 == 1 {
let center_offset = visible_columns as isize / 2;
(focused_idx - center_offset).max(0).min(
(len as isize)
.saturating_sub(visible_columns as isize)
.max(0),
)
} else {
if focused_idx < previous_first_visible {
// focused window is off the left edge, we need to scroll left
focused_idx
} else if focused_idx >= previous_first_visible + visible_columns as isize {
// focused window is off the right edge, we need to scroll right
// and make sure it's the last visible window
(focused_idx + 1 - visible_columns as isize).max(0)
} else {
let previous_first_visible = if latest_layout.is_empty() {
0
} else {
// previous first_visible based on the left position of the first visible window
let left_edge = area.left;
latest_layout
.iter()
.position(|rect| rect.left >= left_edge)
.unwrap_or(0) as isize
};
let focused_idx = focused_idx as isize;
if focused_idx < previous_first_visible {
// focused window is off the left edge, we need to scroll left
focused_idx
} else if focused_idx
>= previous_first_visible + visible_columns as isize
{
// focused window is off the right edge, we need to scroll right
// and make sure it's the last visible window
(focused_idx + 1 - visible_columns as isize).max(0)
} else {
// focused window is already visible, we don't need to scroll
previous_first_visible
}
.min(
(len as isize)
.saturating_sub(visible_columns as isize)
.max(0),
)
};
for i in 0..len {
let position = (i as isize) - first_visible;
let left = area.left + (position as i32 * column_width);
layouts.push(Rect {
left,
top: area.top,
right: column_width,
bottom: area.bottom,
});
// focused window is already visible, we don't need to scroll
previous_first_visible
}
let adjustment = calculate_scrolling_adjustment(resize_dimensions);
layouts.iter_mut().zip(adjustment.iter()).for_each(
|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
},
);
.min(
(len as isize)
.saturating_sub(visible_columns as isize)
.max(0),
)
}
};
for i in 0..len {
let position = (i as isize) - first_visible;
let left = area.left + (position as i32 * column_width);
layouts.push(Rect {
left,
top: area.top,
right: column_width,
bottom: area.bottom,
});
}
let adjustment = calculate_scrolling_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
layouts
}
Self::BSP => recursive_fibonacci(
@@ -166,10 +155,9 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
columns_reverse(&mut layouts);
}
) && let 2.. = len
{
columns_reverse(&mut layouts);
}
layouts
@@ -191,10 +179,9 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Vertical | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
rows_reverse(&mut layouts);
}
) && let 2.. = len
{
rows_reverse(&mut layouts);
}
layouts
@@ -245,25 +232,23 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
) && let 2.. = len
{
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
for rect in rest.iter_mut() {
rect.left = primary.left;
}
primary.left = rest[0].left + rest[0].right;
for rect in rest.iter_mut() {
rect.left = primary.left;
}
primary.left = rest[0].left + rest[0].right;
}
if matches!(
layout_flip,
Some(Axis::Vertical | Axis::HorizontalAndVertical)
) {
if let 3.. = len {
rows_reverse(&mut layouts[1..]);
}
) && let 3.. = len
{
rows_reverse(&mut layouts[1..]);
}
layouts
@@ -317,25 +302,23 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
) && let 2.. = len
{
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
primary.left = rest[0].left;
for rect in rest.iter_mut() {
rect.left = primary.left + primary.right;
}
primary.left = rest[0].left;
for rect in rest.iter_mut() {
rect.left = primary.left + primary.right;
}
}
if matches!(
layout_flip,
Some(Axis::Vertical | Axis::HorizontalAndVertical)
) {
if let 3.. = len {
rows_reverse(&mut layouts[1..]);
}
) && let 3.. = len
{
rows_reverse(&mut layouts[1..]);
}
layouts
@@ -386,25 +369,23 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Vertical | Axis::HorizontalAndVertical)
) {
if let 2.. = len {
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
) && let 2.. = len
{
let (primary, rest) = layouts.split_at_mut(1);
let primary = &mut primary[0];
for rect in rest.iter_mut() {
rect.top = primary.top;
}
primary.top = rest[0].top + rest[0].bottom;
for rect in rest.iter_mut() {
rect.top = primary.top;
}
primary.top = rest[0].top + rest[0].bottom;
}
if matches!(
layout_flip,
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
) {
if let 3.. = len {
columns_reverse(&mut layouts[1..]);
}
) && let 3.. = len
{
columns_reverse(&mut layouts[1..]);
}
layouts
@@ -513,10 +494,9 @@ impl Arrangement for DefaultLayout {
if matches!(
layout_flip,
Some(Axis::Vertical | Axis::HorizontalAndVertical)
) {
if let 4.. = len {
rows_reverse(&mut layouts[2..]);
}
) && let 4.. = len
{
rows_reverse(&mut layouts[2..]);
}
layouts
@@ -782,67 +762,67 @@ fn calculate_resize_adjustments(resize_dimensions: &[Option<Rect>]) -> Vec<Optio
// This needs to be aware of layout flips
for (i, opt) in resize_dimensions.iter().enumerate() {
if let Some(resize_ref) = opt {
if i > 0 {
if resize_ref.left != 0 {
#[allow(clippy::if_not_else)]
let range = if i == 1 {
0..1
} else if i & 1 != 0 {
i - 1..i
} else {
i - 2..i
};
if let Some(resize_ref) = opt
&& i > 0
{
if resize_ref.left != 0 {
#[allow(clippy::if_not_else)]
let range = if i == 1 {
0..1
} else if i & 1 != 0 {
i - 1..i
} else {
i - 2..i
};
for n in range {
let should_adjust = n % 2 == 0;
if should_adjust {
if let Some(Some(adjacent_resize)) = resize_adjustments.get_mut(n) {
adjacent_resize.right += resize_ref.left;
} else {
resize_adjustments[n] = Option::from(Rect {
left: 0,
top: 0,
right: resize_ref.left,
bottom: 0,
});
}
for n in range {
let should_adjust = n % 2 == 0;
if should_adjust {
if let Some(Some(adjacent_resize)) = resize_adjustments.get_mut(n) {
adjacent_resize.right += resize_ref.left;
} else {
resize_adjustments[n] = Option::from(Rect {
left: 0,
top: 0,
right: resize_ref.left,
bottom: 0,
});
}
}
if let Some(rr) = resize_adjustments[i].as_mut() {
rr.left = 0;
}
}
if resize_ref.top != 0 {
let range = if i == 1 {
0..1
} else if i & 1 == 0 {
i - 1..i
} else {
i - 2..i
};
if let Some(rr) = resize_adjustments[i].as_mut() {
rr.left = 0;
}
}
for n in range {
let should_adjust = n % 2 != 0;
if should_adjust {
if let Some(Some(adjacent_resize)) = resize_adjustments.get_mut(n) {
adjacent_resize.bottom += resize_ref.top;
} else {
resize_adjustments[n] = Option::from(Rect {
left: 0,
top: 0,
right: 0,
bottom: resize_ref.top,
});
}
if resize_ref.top != 0 {
let range = if i == 1 {
0..1
} else if i & 1 == 0 {
i - 1..i
} else {
i - 2..i
};
for n in range {
let should_adjust = n % 2 != 0;
if should_adjust {
if let Some(Some(adjacent_resize)) = resize_adjustments.get_mut(n) {
adjacent_resize.bottom += resize_ref.top;
} else {
resize_adjustments[n] = Option::from(Rect {
left: 0,
top: 0,
right: 0,
bottom: resize_ref.top,
});
}
}
}
if let Some(Some(resize)) = resize_adjustments.get_mut(i) {
resize.top = 0;
}
if let Some(Some(resize)) = resize_adjustments.get_mut(i) {
resize.top = 0;
}
}
}
@@ -927,7 +907,7 @@ fn recursive_fibonacci(
right: resized.right,
bottom: resized.bottom,
}]
} else if idx % 2 != 0 {
} else if !idx.is_multiple_of(2) {
let mut res = vec![Rect {
left: resized.left,
top: main_y,

View File

@@ -1,7 +1,7 @@
use crate::config_generation::ApplicationConfiguration;
use crate::config_generation::ApplicationOptions;
use crate::config_generation::MatchingRule;
use color_eyre::Result;
use color_eyre::eyre;
use serde::Deserialize;
use serde::Serialize;
use std::collections::BTreeMap;
@@ -36,12 +36,12 @@ impl DerefMut for ApplicationSpecificConfiguration {
}
impl ApplicationSpecificConfiguration {
pub fn load(pathbuf: &PathBuf) -> Result<Self> {
pub fn load(pathbuf: &PathBuf) -> eyre::Result<Self> {
let content = std::fs::read_to_string(pathbuf)?;
Ok(serde_json::from_str(&content)?)
}
pub fn format(pathbuf: &PathBuf) -> Result<String> {
pub fn format(pathbuf: &PathBuf) -> eyre::Result<String> {
Ok(serde_json::to_string_pretty(&Self::load(pathbuf)?)?)
}
}

View File

@@ -1,5 +1,5 @@
use clap::ValueEnum;
use color_eyre::Result;
use color_eyre::eyre;
use serde::Deserialize;
use serde::Serialize;
use strum::Display;
@@ -142,11 +142,11 @@ impl ApplicationConfiguration {
pub struct ApplicationConfigurationGenerator;
impl ApplicationConfigurationGenerator {
pub fn load(content: &str) -> Result<Vec<ApplicationConfiguration>> {
pub fn load(content: &str) -> eyre::Result<Vec<ApplicationConfiguration>> {
Ok(serde_yaml::from_str(content)?)
}
pub fn format(content: &str) -> Result<String> {
pub fn format(content: &str) -> eyre::Result<String> {
let mut cfgen = Self::load(content)?;
for cfg in &mut cfgen {
cfg.populate_default_matching_strategies();
@@ -156,7 +156,10 @@ impl ApplicationConfigurationGenerator {
Ok(serde_yaml::to_string(&cfgen)?)
}
fn merge(base_content: &str, override_content: &str) -> Result<Vec<ApplicationConfiguration>> {
fn merge(
base_content: &str,
override_content: &str,
) -> eyre::Result<Vec<ApplicationConfiguration>> {
let base_cfgen = Self::load(base_content)?;
let override_cfgen = Self::load(override_content)?;
@@ -182,7 +185,7 @@ impl ApplicationConfigurationGenerator {
pub fn generate_pwsh(
base_content: &str,
override_content: Option<&str>,
) -> Result<Vec<String>> {
) -> eyre::Result<Vec<String>> {
let mut cfgen = if let Some(override_content) = override_content {
Self::merge(base_content, override_content)?
} else {
@@ -233,7 +236,10 @@ impl ApplicationConfigurationGenerator {
Ok(lines)
}
pub fn generate_ahk(base_content: &str, override_content: Option<&str>) -> Result<Vec<String>> {
pub fn generate_ahk(
base_content: &str,
override_content: Option<&str>,
) -> eyre::Result<Vec<String>> {
let mut cfgen = if let Some(override_content) = override_content {
Self::merge(base_content, override_content)?
} else {

View File

@@ -1,3 +1,7 @@
use color_eyre::eyre;
use color_eyre::eyre::bail;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
@@ -5,12 +9,6 @@ use std::ops::Deref;
use std::ops::DerefMut;
use std::path::Path;
use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::Result;
use serde::Deserialize;
use serde::Serialize;
use super::Rect;
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
@@ -32,7 +30,7 @@ impl DerefMut for CustomLayout {
}
impl CustomLayout {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self> {
pub fn from_path<P: AsRef<Path>>(path: P) -> eyre::Result<Self> {
let path = path.as_ref();
let layout: Self = match path.extension() {
Some(extension) if extension == "yaml" || extension == "yml" => {
@@ -41,7 +39,7 @@ impl CustomLayout {
Some(extension) if extension == "json" => {
serde_json::from_reader(BufReader::new(File::open(path)?))?
}
_ => return Err(anyhow!("custom layouts must be json or yaml files")),
_ => bail!("custom layouts must be json or yaml files"),
};
if !layout.is_valid() {

View File

@@ -37,6 +37,8 @@ pub struct LayoutOptions {
pub struct ScrollingLayoutOptions {
/// Desired number of visible columns (default: 3)
pub columns: usize,
/// With an odd number of visible columns, keep the focused window column centered
pub center_focused_column: Option<bool>,
}
impl DefaultLayout {

View File

@@ -1,9 +1,9 @@
use super::DefaultLayout;
use super::OperationDirection;
use super::custom_layout::Column;
use super::custom_layout::ColumnSplit;
use super::custom_layout::ColumnSplitWithCapacity;
use super::custom_layout::CustomLayout;
use super::DefaultLayout;
use super::OperationDirection;
pub trait Direction {
fn index_in_direction(
@@ -105,7 +105,7 @@ impl Direction for DefaultLayout {
Self::Scrolling => false,
},
OperationDirection::Down => match self {
Self::BSP => idx != count - 1 && idx % 2 != 0,
Self::BSP => idx != count - 1 && !idx.is_multiple_of(2),
Self::Columns => false,
Self::Rows => idx != count - 1,
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != count - 1,
@@ -125,7 +125,7 @@ impl Direction for DefaultLayout {
Self::Scrolling => idx != 0,
},
OperationDirection::Right => match self {
Self::BSP => idx % 2 == 0 && idx != count - 1,
Self::BSP => idx.is_multiple_of(2) && idx != count - 1,
Self::Columns => idx != count - 1,
Self::Rows => false,
Self::VerticalStack => idx == 0,
@@ -149,7 +149,7 @@ impl Direction for DefaultLayout {
) -> usize {
match self {
Self::BSP => {
if idx % 2 == 0 {
if idx.is_multiple_of(2) {
idx - 1
} else {
idx - 2
@@ -193,7 +193,7 @@ impl Direction for DefaultLayout {
) -> usize {
match self {
Self::BSP => {
if idx % 2 == 0 {
if idx.is_multiple_of(2) {
idx - 2
} else {
idx - 1

View File

@@ -6,14 +6,14 @@ use std::path::PathBuf;
use std::str::FromStr;
use clap::ValueEnum;
use color_eyre::Result;
use color_eyre::eyre;
use serde::Deserialize;
use serde::Serialize;
use strum::Display;
use strum::EnumString;
use crate::animation::prefix::AnimationPrefix;
use crate::KomorebiTheme;
use crate::animation::prefix::AnimationPrefix;
pub use animation::AnimationStyle;
pub use arrangement::Arrangement;
pub use arrangement::Axis;
@@ -27,10 +27,10 @@ pub use default_layout::DefaultLayout;
pub use direction::Direction;
pub use layout::Layout;
pub use operation_direction::OperationDirection;
pub use pathext::replace_env_in_path;
pub use pathext::resolve_option_hashmap_usize_path;
pub use pathext::PathExt;
pub use pathext::ResolvedPathBuf;
pub use pathext::replace_env_in_path;
pub use pathext::resolve_option_hashmap_usize_path;
pub use rect::Rect;
pub mod animation;
@@ -248,7 +248,7 @@ pub enum SocketMessage {
}
impl SocketMessage {
pub fn as_bytes(&self) -> Result<Vec<u8>> {
pub fn as_bytes(&self) -> eyre::Result<Vec<u8>> {
Ok(serde_json::to_string(self)?.as_bytes().to_vec())
}
}
@@ -256,7 +256,7 @@ impl SocketMessage {
impl FromStr for SocketMessage {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
fn from_str(s: &str) -> eyre::Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
@@ -550,7 +550,9 @@ mod tests {
#[test]
fn deserializes() {
// Set a variable for testing
std::env::set_var("VAR", "VALUE");
unsafe {
std::env::set_var("VAR", "VALUE");
}
let json = r#"{"type":"WorkspaceLayoutCustomRule","content":[0,0,0,"/path/%VAR%/d"]}"#;
let message: SocketMessage = serde_json::from_str(json).unwrap();

View File

@@ -6,8 +6,8 @@ use serde::Serialize;
use strum::Display;
use strum::EnumString;
use super::direction::Direction;
use super::Axis;
use super::direction::Direction;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

View File

@@ -1,5 +1,4 @@
use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::path::Component;
use std::path::Path;
@@ -58,7 +57,7 @@ impl<P: AsRef<Path>> PathExt for P {
// if component is a variable, get the value from the environment
if let Some(var) = var {
let var = unsafe { OsStr::from_encoded_bytes_unchecked(var) };
if let Some(value) = env::var_os(var) {
if let Some(value) = std::env::var_os(var) {
out.push(value);
continue;
}
@@ -127,8 +126,8 @@ impl serde_with::schemars_0_8::JsonSchemaAs<PathBuf> for ResolvedPathBuf {
"PathBuf".to_owned()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<PathBuf as schemars::JsonSchema>::json_schema(gen)
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::schema::Schema {
<PathBuf as schemars::JsonSchema>::json_schema(generator)
}
}
@@ -165,7 +164,9 @@ mod tests {
#[test]
fn resolves_env_vars() {
// Set a variable for testing
std::env::set_var("VAR", "VALUE");
unsafe {
std::env::set_var("VAR", "VALUE");
}
// %VAR% format
assert_eq!(resolve("/path/%VAR%/d"), expected("/path/VALUE/d"));
@@ -183,7 +184,9 @@ mod tests {
assert_eq!(resolve("/path/$ASD/to/d"), expected("/path/$ASD/to/d"));
// Set a $env:USERPROFILE variable for testing
std::env::set_var("USERPROFILE", "C:\\Users\\user");
unsafe {
std::env::set_var("USERPROFILE", "C:\\Users\\user");
}
// ~ and $HOME should be replaced with $Env:USERPROFILE
assert_eq!(resolve("~"), expected("C:\\Users\\user"));

View File

@@ -44,13 +44,15 @@ pub fn send_notification(hwnd: isize) {
}
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
std::thread::spawn(move || loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});

View File

@@ -39,12 +39,12 @@ use std::io::Write;
use std::net::TcpStream;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
use std::sync::Arc;
pub use core::*;
pub use komorebi_themes::colour::*;
@@ -62,7 +62,7 @@ use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy;
use crate::core::config_generation::WorkspaceMatchingRule;
use color_eyre::Result;
use color_eyre::eyre;
use crossbeam_utils::atomic::AtomicCell;
use os_info::Version;
use parking_lot::Mutex;
@@ -72,8 +72,8 @@ use serde::Deserialize;
use serde::Serialize;
use uds_windows::UnixStream;
use which::which;
use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey;
use winreg::enums::HKEY_CURRENT_USER;
lazy_static! {
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
@@ -211,11 +211,10 @@ lazy_static! {
pub static ref AHK_EXE: String = {
let mut ahk: String = String::from("autohotkey.exe");
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE") {
if which(&komorebi_ahk_exe).is_ok() {
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE")
&& which(&komorebi_ahk_exe).is_ok() {
ahk = komorebi_ahk_exe;
}
}
ahk
};
@@ -330,7 +329,10 @@ pub struct Notification {
pub state: State,
}
pub fn notify_subscribers(notification: Notification, state_has_been_modified: bool) -> Result<()> {
pub fn notify_subscribers(
notification: Notification,
state_has_been_modified: bool,
) -> eyre::Result<()> {
let is_override_event = matches!(
notification.event,
NotificationEvent::Socket(SocketMessage::AddSubscriberSocket(_))
@@ -411,7 +413,7 @@ pub fn notify_subscribers(notification: Notification, state_has_been_modified: b
Ok(())
}
pub fn load_configuration() -> Result<()> {
pub fn load_configuration() -> eyre::Result<()> {
let config_pwsh = HOME_DIR.join("komorebi.ps1");
let config_ahk = HOME_DIR.join("komorebi.ahk");

View File

@@ -10,31 +10,36 @@
use std::env::temp_dir;
use std::net::Shutdown;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::Ordering;
#[cfg(feature = "deadlock_detection")]
use std::time::Duration;
use clap::Parser;
use clap::ValueEnum;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use color_eyre::eyre;
use color_eyre::eyre::bail;
use crossbeam_utils::Backoff;
use komorebi::animation::AnimationEngine;
use komorebi::animation::ANIMATION_ENABLED_GLOBAL;
use komorebi::animation::ANIMATION_ENABLED_PER_ANIMATION;
use komorebi::animation::AnimationEngine;
use komorebi::replace_env_in_path;
use parking_lot::Mutex;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
use parking_lot::Mutex;
use serde::Deserialize;
use sysinfo::Process;
use sysinfo::ProcessesToUpdate;
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::layer::SubscriberExt;
use uds_windows::UnixStream;
use komorebi::CUSTOM_FFM;
use komorebi::DATA_DIR;
use komorebi::HOME_DIR;
use komorebi::INITIAL_CONFIGURATION_LOADED;
use komorebi::SESSION_ID;
use komorebi::border_manager;
use komorebi::focus_manager;
use komorebi::load_configuration;
@@ -52,30 +57,29 @@ use komorebi::window_manager::State;
use komorebi::window_manager::WindowManager;
use komorebi::windows_api::WindowsApi;
use komorebi::winevent_listener;
use komorebi::CUSTOM_FFM;
use komorebi::DATA_DIR;
use komorebi::HOME_DIR;
use komorebi::INITIAL_CONFIGURATION_LOADED;
use komorebi::SESSION_ID;
fn setup(log_level: LogLevel) -> Result<(WorkerGuard, WorkerGuard)> {
fn setup(log_level: LogLevel) -> eyre::Result<(WorkerGuard, WorkerGuard)> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
unsafe {
std::env::set_var("RUST_LIB_BACKTRACE", "1");
}
}
color_eyre::install()?;
if std::env::var("RUST_LOG").is_err() {
std::env::set_var(
"RUST_LOG",
match log_level {
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Trace => "trace",
},
);
unsafe {
std::env::set_var(
"RUST_LOG",
match log_level {
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Trace => "trace",
},
);
}
}
let appender = tracing_appender::rolling::daily(std::env::temp_dir(), "komorebi_plaintext.log");
@@ -134,20 +138,22 @@ fn setup(log_level: LogLevel) -> Result<(WorkerGuard, WorkerGuard)> {
#[tracing::instrument]
fn detect_deadlocks() {
// Create a background thread which checks for deadlocks every 10s
std::thread::spawn(move || loop {
tracing::info!("running deadlock detector");
std::thread::sleep(Duration::from_secs(5));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
std::thread::spawn(move || {
loop {
tracing::info!("running deadlock detector");
std::thread::sleep(Duration::from_secs(5));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
}
tracing::error!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
tracing::error!("deadlock #{}", i);
for t in threads {
tracing::error!("thread id: {:#?}", t.thread_id());
tracing::error!("{:#?}", t.backtrace());
tracing::error!("{} deadlocks detected", deadlocks.len());
for (i, threads) in deadlocks.iter().enumerate() {
tracing::error!("deadlock #{}", i);
for t in threads {
tracing::error!("thread id: {:#?}", t.thread_id());
tracing::error!("{:#?}", t.backtrace());
}
}
}
});
@@ -190,7 +196,7 @@ struct Opts {
#[tracing::instrument]
#[allow(clippy::cognitive_complexity)]
fn main() -> Result<()> {
fn main() -> eyre::Result<()> {
let opts: Opts = Opts::parse();
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
@@ -210,9 +216,7 @@ fn main() -> Result<()> {
}
if set_foreground_window_retries == 0 {
return Err(anyhow!(
"failed call to AllowSetForegroundWindow after 5 retries"
));
bail!("failed call to AllowSetForegroundWindow after 5 retries");
}
}
@@ -229,15 +233,17 @@ fn main() -> Result<()> {
if matched_procs.len() > 1 {
let mut len = matched_procs.len();
for proc in matched_procs {
if let Some(executable_path) = proc.exe() {
if executable_path.to_string_lossy().contains("shims") {
len -= 1;
}
if let Some(executable_path) = proc.exe()
&& executable_path.to_string_lossy().contains("shims")
{
len -= 1;
}
}
if len > 1 {
tracing::error!("komorebi.exe is already running, please exit the existing process before starting a new one");
tracing::error!(
"komorebi.exe is already running, please exit the existing process before starting a new one"
);
std::process::exit(1);
}
}

View File

@@ -2,13 +2,9 @@ use std::collections::HashMap;
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::eyre;
use color_eyre::eyre::OptionExt;
use color_eyre::eyre::bail;
use color_eyre::Result;
use getset::CopyGetters;
use getset::Getters;
use getset::MutGetters;
use getset::Setters;
use serde::Deserialize;
use serde::Serialize;
@@ -17,58 +13,40 @@ use crate::border_manager::BORDER_OFFSET;
use crate::border_manager::BORDER_WIDTH;
use crate::core::Rect;
use crate::container::Container;
use crate::ring::Ring;
use crate::workspace::Workspace;
use crate::workspace::WorkspaceGlobals;
use crate::workspace::WorkspaceLayer;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::DefaultLayout;
use crate::FloatingLayerBehaviour;
use crate::Layout;
use crate::OperationDirection;
use crate::Wallpaper;
use crate::WindowsApi;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::container::Container;
use crate::ring::Ring;
use crate::workspace::Workspace;
use crate::workspace::WorkspaceGlobals;
use crate::workspace::WorkspaceLayer;
#[derive(
Debug, Clone, Serialize, Deserialize, Getters, CopyGetters, MutGetters, Setters, PartialEq,
)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Monitor {
#[getset(get_copy = "pub", set = "pub")]
pub id: isize,
#[getset(get = "pub", set = "pub")]
pub name: String,
#[getset(get = "pub", set = "pub")]
pub device: String,
#[getset(get = "pub", set = "pub")]
pub device_id: String,
#[getset(get = "pub", set = "pub")]
pub serial_number_id: Option<String>,
#[getset(get = "pub", set = "pub")]
pub size: Rect,
#[getset(get = "pub", set = "pub")]
pub work_area_size: Rect,
#[getset(get_copy = "pub", set = "pub")]
pub work_area_offset: Option<Rect>,
#[getset(get_copy = "pub", set = "pub")]
pub window_based_work_area_offset: Option<Rect>,
#[getset(get_copy = "pub", set = "pub")]
pub window_based_work_area_offset_limit: isize,
pub workspaces: Ring<Workspace>,
#[serde(skip_serializing_if = "Option::is_none")]
#[getset(get_copy = "pub", set = "pub")]
pub last_focused_workspace: Option<usize>,
#[getset(get_mut = "pub")]
pub workspace_names: HashMap<usize, String>,
#[getset(get_copy = "pub", set = "pub")]
pub container_padding: Option<i32>,
#[getset(get_copy = "pub", set = "pub")]
pub workspace_padding: Option<i32>,
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub wallpaper: Option<Wallpaper>,
#[getset(get_copy = "pub", set = "pub")]
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
}
@@ -175,23 +153,23 @@ impl Monitor {
pub fn focused_workspace_name(&self) -> Option<String> {
self.focused_workspace()
.map(|w| w.name().clone())
.map(|w| w.name.clone())
.unwrap_or(None)
}
pub fn focused_workspace_layout(&self) -> Option<Layout> {
self.focused_workspace().and_then(|workspace| {
if *workspace.tile() {
Some(workspace.layout().clone())
if workspace.tile {
Some(workspace.layout.clone())
} else {
None
}
})
}
pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> {
pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> eyre::Result<()> {
let focused_idx = self.focused_workspace_idx();
let hmonitor = self.id();
let hmonitor = self.id;
let monitor_wp = self.wallpaper.clone();
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
if i == focused_idx {
@@ -207,10 +185,10 @@ impl Monitor {
/// Updates the `globals` field of all workspaces
pub fn update_workspaces_globals(&mut self, offset: Option<Rect>) {
let container_padding = self
.container_padding()
.container_padding
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
let workspace_padding = self
.workspace_padding()
.workspace_padding
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
let (border_width, border_offset) = {
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
@@ -222,11 +200,11 @@ impl Monitor {
(0, 0)
}
};
let work_area = *self.work_area_size();
let work_area = self.work_area_size;
let work_area_offset = self.work_area_offset.or(offset);
let window_based_work_area_offset = self.window_based_work_area_offset();
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
let floating_layer_behaviour = self.floating_layer_behaviour();
let window_based_work_area_offset = self.window_based_work_area_offset;
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit;
let floating_layer_behaviour = self.floating_layer_behaviour;
for workspace in self.workspaces_mut() {
workspace.globals = WorkspaceGlobals {
@@ -246,10 +224,10 @@ impl Monitor {
/// Updates the `globals` field of workspace with index `workspace_idx`
pub fn update_workspace_globals(&mut self, workspace_idx: usize, offset: Option<Rect>) {
let container_padding = self
.container_padding()
.container_padding
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
let workspace_padding = self
.workspace_padding()
.workspace_padding
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
let (border_width, border_offset) = {
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
@@ -261,11 +239,11 @@ impl Monitor {
(0, 0)
}
};
let work_area = *self.work_area_size();
let work_area = self.work_area_size;
let work_area_offset = self.work_area_offset.or(offset);
let window_based_work_area_offset = self.window_based_work_area_offset();
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
let floating_layer_behaviour = self.floating_layer_behaviour();
let window_based_work_area_offset = self.window_based_work_area_offset;
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit;
let floating_layer_behaviour = self.floating_layer_behaviour;
if let Some(workspace) = self.workspaces_mut().get_mut(workspace_idx) {
workspace.globals = WorkspaceGlobals {
@@ -286,14 +264,14 @@ impl Monitor {
&mut self,
container: Container,
workspace_idx: Option<usize>,
) -> Result<()> {
) -> eyre::Result<()> {
let workspace = if let Some(idx) = workspace_idx {
self.workspaces_mut()
.get_mut(idx)
.ok_or_else(|| anyhow!("there is no workspace at index {}", idx))?
.ok_or_eyre(format!("there is no workspace at index {idx}"))?
} else {
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?
.ok_or_eyre("there is no workspace")?
};
workspace.add_container_to_back(container);
@@ -310,21 +288,21 @@ impl Monitor {
container: Container,
workspace_idx: Option<usize>,
direction: OperationDirection,
) -> Result<()> {
) -> eyre::Result<()> {
let workspace = if let Some(idx) = workspace_idx {
self.workspaces_mut()
.get_mut(idx)
.ok_or_else(|| anyhow!("there is no workspace at index {}", idx))?
.ok_or_eyre(format!("there is no workspace at index {idx}"))?
} else {
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?
.ok_or_eyre("there is no workspace")?
};
match direction {
OperationDirection::Left => {
// insert the container into the workspace on the monitor at the back (or rightmost position)
// if we are moving across a boundary to the left (back = right side of the target)
match workspace.layout() {
match workspace.layout {
Layout::Default(layout) => match layout {
DefaultLayout::RightMainVerticalStack => {
workspace.add_container_to_front(container);
@@ -348,7 +326,7 @@ impl Monitor {
OperationDirection::Right => {
// insert the container into the workspace on the monitor at the front (or leftmost position)
// if we are moving across a boundary to the right (front = left side of the target)
match workspace.layout() {
match workspace.layout {
Layout::Default(layout) => {
let target_index = layout.leftmost_index(workspace.containers().len());
@@ -412,12 +390,12 @@ impl Monitor {
target_workspace_idx: usize,
follow: bool,
direction: Option<OperationDirection>,
) -> Result<()> {
) -> eyre::Result<()> {
let workspace = self
.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?;
.ok_or_eyre("there is no workspace")?;
if workspace.maximized_window().is_some() {
if workspace.maximized_window.is_some() {
bail!("cannot move native maximized window to another monitor or workspace");
}
@@ -440,12 +418,12 @@ impl Monitor {
};
target_workspace.floating_windows_mut().push_back(window);
target_workspace.set_layer(WorkspaceLayer::Floating);
target_workspace.layer = WorkspaceLayer::Floating;
}
} else {
let container = workspace
.remove_focused_container()
.ok_or_else(|| anyhow!("there is no container"))?;
.ok_or_eyre("there is no container")?;
let workspaces = self.workspaces_mut();
@@ -458,7 +436,7 @@ impl Monitor {
Some(workspace) => workspace,
};
if target_workspace.monocle_container().is_some() {
if target_workspace.monocle_container.is_some() {
for container in target_workspace.containers_mut() {
container.restore();
}
@@ -470,7 +448,7 @@ impl Monitor {
target_workspace.reintegrate_monocle_container()?;
}
target_workspace.set_layer(WorkspaceLayer::Tiling);
target_workspace.layer = WorkspaceLayer::Tiling;
if let Some(direction) = direction {
self.add_container_with_direction(
@@ -491,7 +469,7 @@ impl Monitor {
}
#[tracing::instrument(skip(self))]
pub fn focus_workspace(&mut self, idx: usize) -> Result<()> {
pub fn focus_workspace(&mut self, idx: usize) -> eyre::Result<()> {
tracing::info!("focusing workspace");
{
@@ -500,7 +478,7 @@ impl Monitor {
if workspaces.get(idx).is_none() {
workspaces.resize(idx + 1, Workspace::default());
}
self.set_last_focused_workspace(Some(self.workspaces.focused_idx()));
self.last_focused_workspace = Some(self.workspaces.focused_idx());
self.workspaces.focus(idx);
}
@@ -510,8 +488,8 @@ impl Monitor {
if name.is_some() {
self.workspaces_mut()
.get_mut(idx)
.ok_or_else(|| anyhow!("there is no workspace"))?
.set_name(name);
.ok_or_eyre("there is no workspace")?
.name = name;
}
}
@@ -522,9 +500,9 @@ impl Monitor {
self.workspaces().len()
}
pub fn update_focused_workspace(&mut self, offset: Option<Rect>) -> Result<()> {
let offset = if self.work_area_offset().is_some() {
self.work_area_offset()
pub fn update_focused_workspace(&mut self, offset: Option<Rect>) -> eyre::Result<()> {
let offset = if self.work_area_offset.is_some() {
self.work_area_offset
} else {
offset
};
@@ -532,7 +510,7 @@ impl Monitor {
let focused_workspace_idx = self.focused_workspace_idx();
self.update_workspace_globals(focused_workspace_idx, offset);
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?
.ok_or_eyre("there is no workspace")?
.update()?;
Ok(())

View File

@@ -1,7 +1,6 @@
use std::sync::mpsc;
use std::time::Duration;
use windows::core::PCWSTR;
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_DISPLAY_ADAPTER;
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_MONITOR;
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL;
@@ -11,10 +10,6 @@ use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::System::Power::POWERBROADCAST_SETTING;
use windows::Win32::System::SystemServices::GUID_LIDSWITCH_STATE_CHANGE;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::DBT_CONFIGCHANGED;
@@ -23,6 +18,9 @@ use windows::Win32::UI::WindowsAndMessaging::DBT_DEVICEREMOVECOMPLETE;
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVNODES_CHANGED;
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVTYP_DEVICEINTERFACE;
use windows::Win32::UI::WindowsAndMessaging::DEV_BROADCAST_DEVICEINTERFACE_W;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::PBT_APMRESUMEAUTOMATIC;
use windows::Win32::UI::WindowsAndMessaging::PBT_APMRESUMESUSPEND;
@@ -30,6 +28,7 @@ use windows::Win32::UI::WindowsAndMessaging::PBT_APMSUSPEND;
use windows::Win32::UI::WindowsAndMessaging::PBT_POWERSETTINGCHANGE;
use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETWORKAREA;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::WM_DEVICECHANGE;
use windows::Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE;
use windows::Win32::UI::WindowsAndMessaging::WM_POWERBROADCAST;
@@ -38,10 +37,11 @@ use windows::Win32::UI::WindowsAndMessaging::WM_WTSSESSION_CHANGE;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_LOCK;
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_UNLOCK;
use windows::core::PCWSTR;
use crate::WindowsApi;
use crate::monitor_reconciliator;
use crate::windows_api;
use crate::WindowsApi;
// This is a hidden window specifically spawned to listen to system-wide events related to monitors
#[derive(Debug, Clone, Copy)]
@@ -224,14 +224,18 @@ impl Hidden {
WM_WTSSESSION_CHANGE => {
match wparam.0 as u32 {
WTS_SESSION_LOCK => {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_LOCK - screen locked");
tracing::debug!(
"WM_WTSSESSION_CHANGE event received with WTS_SESSION_LOCK - screen locked"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::MonitorNotification::SessionLocked,
);
}
WTS_SESSION_UNLOCK => {
tracing::debug!("WM_WTSSESSION_CHANGE event received with WTS_SESSION_UNLOCK - screen unlocked");
tracing::debug!(
"WM_WTSSESSION_CHANGE event received with WTS_SESSION_UNLOCK - screen unlocked"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::MonitorNotification::SessionUnlocked,
@@ -251,7 +255,8 @@ impl Hidden {
// and resolution changes here
WM_DISPLAYCHANGE => {
tracing::debug!(
"WM_DISPLAYCHANGE event received with wparam: {}- work area or display resolution changed", wparam.0
"WM_DISPLAYCHANGE event received with wparam: {}- work area or display resolution changed",
wparam.0
);
monitor_reconciliator::send_notification(
@@ -265,8 +270,8 @@ impl Hidden {
#[allow(clippy::cast_possible_truncation)]
if wparam.0 as u32 == SPI_SETWORKAREA.0 {
tracing::debug!(
"WM_SETTINGCHANGE event received with SPI_SETWORKAREA - work area changed (probably butterytaskbar or something similar)"
);
"WM_SETTINGCHANGE event received with SPI_SETWORKAREA - work area changed (probably butterytaskbar or something similar)"
);
monitor_reconciliator::send_notification(
monitor_reconciliator::MonitorNotification::WorkAreaChanged,

View File

@@ -1,5 +1,13 @@
#![deny(clippy::unwrap_used, clippy::expect_used)]
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::WORKSPACE_MATCHING_RULES;
use crate::WindowManager;
use crate::WindowsApi;
use crate::border_manager;
use crate::config_generation::WorkspaceMatchingRule;
use crate::core::Rect;
@@ -7,14 +15,6 @@ use crate::monitor;
use crate::monitor::Monitor;
use crate::monitor_reconciliator::hidden::Hidden;
use crate::notify_subscribers;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::WindowManager;
use crate::WindowsApi;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
use crate::WORKSPACE_MATCHING_RULES;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicConsume;
@@ -22,10 +22,10 @@ use parking_lot::Mutex;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
pub mod hidden;
@@ -70,10 +70,10 @@ pub fn send_notification(notification: MonitorNotification) {
pub fn insert_in_monitor_cache(serial_or_device_id: &str, monitor: Monitor) {
let dip = DISPLAY_INDEX_PREFERENCES.read();
let mut dip_ids = dip.values();
let preferred_id = if dip_ids.any(|id| id == monitor.device_id()) {
monitor.device_id().clone()
} else if dip_ids.any(|id| Some(id) == monitor.serial_number_id().as_ref()) {
monitor.serial_number_id().clone().unwrap_or_default()
let preferred_id = if dip_ids.any(|id| id.eq(&monitor.device_id)) {
monitor.device_id.clone()
} else if dip_ids.any(|id| Some(id) == monitor.serial_number_id.as_ref()) {
monitor.serial_number_id.clone().unwrap_or_default()
} else {
serial_or_device_id.to_string()
};
@@ -100,12 +100,12 @@ where
}
for d in &all_displays {
if let Some(id) = &d.serial_number_id {
if serial_id_map.get(id).copied().unwrap_or_default() > 1 {
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
if !dupes.contains(id) {
(*dupes).push(id.clone());
}
if let Some(id) = &d.serial_number_id
&& serial_id_map.get(id).copied().unwrap_or_default() > 1
{
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
if !dupes.contains(id) {
(*dupes).push(id.clone());
}
}
}
@@ -155,16 +155,18 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Re
tracing::info!("created hidden window to listen for monitor-related events");
std::thread::spawn(move || loop {
match handle_notifications(wm.clone(), win32_display_data::connected_displays_all) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
if cfg!(debug_assertions) {
tracing::error!("restarting failed thread: {:?}", error)
} else {
tracing::error!("restarting failed thread: {}", error)
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone(), win32_display_data::connected_displays_all) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
if cfg!(debug_assertions) {
tracing::error!("restarting failed thread: {:?}", error)
} else {
tracing::error!("restarting failed thread: {}", error)
}
}
}
}
@@ -219,27 +221,27 @@ where
let mut should_update = false;
// Update work areas as necessary
if let Ok(reference) = WindowsApi::monitor(monitor.id()) {
if reference.work_area_size() != monitor.work_area_size() {
monitor.set_work_area_size(Rect {
left: reference.work_area_size().left,
top: reference.work_area_size().top,
right: reference.work_area_size().right,
bottom: reference.work_area_size().bottom,
});
if let Ok(reference) = WindowsApi::monitor(monitor.id)
&& reference.work_area_size != monitor.work_area_size
{
monitor.work_area_size = Rect {
left: reference.work_area_size.left,
top: reference.work_area_size.top,
right: reference.work_area_size.right,
bottom: reference.work_area_size.bottom,
};
should_update = true;
}
should_update = true;
}
if should_update {
tracing::info!("updated work area for {}", monitor.device_id());
tracing::info!("updated work area for {}", monitor.device_id);
monitor.update_focused_workspace(offset)?;
border_manager::send_notification(None);
} else {
tracing::debug!(
"work areas match, reconciliation not required for {}",
monitor.device_id()
monitor.device_id
);
}
}
@@ -251,25 +253,25 @@ where
let mut should_update = false;
// Update sizes and work areas as necessary
if let Ok(reference) = WindowsApi::monitor(monitor.id()) {
if reference.work_area_size() != monitor.work_area_size() {
monitor.set_work_area_size(Rect {
left: reference.work_area_size().left,
top: reference.work_area_size().top,
right: reference.work_area_size().right,
bottom: reference.work_area_size().bottom,
});
if let Ok(reference) = WindowsApi::monitor(monitor.id) {
if reference.work_area_size != monitor.work_area_size {
monitor.work_area_size = Rect {
left: reference.work_area_size.left,
top: reference.work_area_size.top,
right: reference.work_area_size.right,
bottom: reference.work_area_size.bottom,
};
should_update = true;
}
if reference.size() != monitor.size() {
monitor.set_size(Rect {
left: reference.size().left,
top: reference.size().top,
right: reference.size().right,
bottom: reference.size().bottom,
});
if reference.size != monitor.size {
monitor.size = Rect {
left: reference.size.left,
top: reference.size.top,
right: reference.size.right,
bottom: reference.size.bottom,
};
should_update = true;
}
@@ -278,7 +280,7 @@ where
if should_update {
tracing::info!(
"updated monitor resolution/scaling for {}",
monitor.device_id()
monitor.device_id
);
monitor.update_focused_workspace(offset)?;
@@ -286,7 +288,7 @@ where
} else {
tracing::debug!(
"resolutions match, reconciliation not required for {}",
monitor.device_id()
monitor.device_id
);
}
}
@@ -311,21 +313,21 @@ where
for monitor in wm.monitors_mut() {
for attached in &attached_devices {
let serial_number_ids_match = if let (Some(attached_snid), Some(m_snid)) =
(attached.serial_number_id(), monitor.serial_number_id())
(&attached.serial_number_id, &monitor.serial_number_id)
{
attached_snid.eq(m_snid)
} else {
false
};
if serial_number_ids_match || attached.device_id().eq(monitor.device_id()) {
monitor.set_id(attached.id());
monitor.set_device(attached.device().clone());
monitor.set_device_id(attached.device_id().clone());
monitor.set_serial_number_id(attached.serial_number_id().clone());
monitor.set_name(attached.name().clone());
monitor.set_size(*attached.size());
monitor.set_work_area_size(*attached.work_area_size());
if serial_number_ids_match || attached.device_id.eq(&monitor.device_id) {
monitor.id = attached.id;
monitor.device = attached.device.clone();
monitor.device_id = attached.device_id.clone();
monitor.serial_number_id = attached.serial_number_id.clone();
monitor.name = attached.name.clone();
monitor.size = attached.size;
monitor.work_area_size = attached.work_area_size;
}
}
}
@@ -359,13 +361,13 @@ where
for (m_idx, m) in wm.monitors().iter().enumerate() {
if !attached_devices.iter().any(|attached| {
attached.serial_number_id().eq(m.serial_number_id())
|| attached.device_id().eq(m.device_id())
attached.serial_number_id.eq(&m.serial_number_id)
|| attached.device_id.eq(&m.device_id)
}) {
let id = m
.serial_number_id()
.serial_number_id
.as_ref()
.map_or(m.device_id().clone(), |sn| sn.clone());
.map_or(m.device_id.clone(), |sn| sn.clone());
newly_removed_displays.push(id.clone());
@@ -392,7 +394,7 @@ where
}
}
if let Some(maximized) = workspace.maximized_window() {
if let Some(maximized) = &workspace.maximized_window {
windows_to_remove.push(maximized.hwnd);
// Minimize the focused window since Windows might try
// to move it to another monitor if it was focused.
@@ -401,7 +403,7 @@ where
}
}
if let Some(container) = workspace.monocle_container() {
if let Some(container) = &workspace.monocle_container {
for window in container.windows() {
windows_to_remove.push(window.hwnd);
}
@@ -440,10 +442,10 @@ where
// the user set as preference as the id.
let dip = DISPLAY_INDEX_PREFERENCES.read();
let mut dip_ids = dip.values();
let preferred_id = if dip_ids.any(|id| id == m.device_id()) {
m.device_id().clone()
} else if dip_ids.any(|id| Some(id) == m.serial_number_id().as_ref()) {
m.serial_number_id().clone().unwrap_or_default()
let preferred_id = if dip_ids.any(|id| id.eq(&m.device_id)) {
m.device_id.clone()
} else if dip_ids.any(|id| Some(id) == m.serial_number_id.as_ref()) {
m.serial_number_id.clone().unwrap_or_default()
} else {
id
};
@@ -458,8 +460,8 @@ where
// After we have cached them, remove them from our state
wm.monitors_mut().retain(|m| {
!newly_removed_displays.iter().any(|id| {
m.serial_number_id().as_ref().is_some_and(|sn| sn == id)
|| m.device_id() == id
m.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|| m.device_id.eq(id)
})
});
}
@@ -490,7 +492,7 @@ where
let post_removal_device_ids = wm
.monitors()
.iter()
.map(Monitor::device_id)
.map(|m| &m.device_id)
.cloned()
.collect::<Vec<_>>();
@@ -513,21 +515,23 @@ where
// Look in the updated state for new monitors
for (i, m) in wm.monitors_mut().iter_mut().enumerate() {
let device_id = m.device_id();
let device_id = &m.device_id;
// We identify a new monitor when we encounter a new device id
if !post_removal_device_ids.contains(device_id) {
let mut cache_hit = false;
let mut cached_id = String::new();
// Check if that device id exists in the cache for this session
if let Some((id, cached)) = monitor_cache.get_key_value(device_id).or(m
.serial_number_id()
.serial_number_id
.as_ref()
.and_then(|sn| monitor_cache.get_key_value(sn)))
{
cache_hit = true;
cached_id = id.clone();
tracing::info!("found monitor and workspace configuration for {id} in the monitor cache, applying");
tracing::info!(
"found monitor and workspace configuration for {id} in the monitor cache, applying"
);
// If it does, update the cached monitor info with the new one and
// load the cached monitor removing any window that has since been
@@ -608,24 +612,24 @@ where
}
}
if let Some(window) = workspace.maximized_window() {
if let Some(window) = &workspace.maximized_window {
if window.exe().is_err()
|| known_hwnds.contains_key(&window.hwnd)
{
workspace.set_maximized_window(None);
workspace.maximized_window = None;
} else if is_focused_workspace {
WindowsApi::restore_window(window.hwnd);
}
}
if let Some(container) = workspace.monocle_container_mut() {
if let Some(container) = &mut workspace.monocle_container {
container.windows_mut().retain(|window| {
window.exe().is_ok()
&& !known_hwnds.contains_key(&window.hwnd)
});
if container.windows().is_empty() {
workspace.set_monocle_container(None);
workspace.monocle_container = None;
} else if is_focused_workspace {
if let Some(window) = container.focused_window() {
WindowsApi::restore_window(window.hwnd);
@@ -657,7 +661,7 @@ where
let mut workspace_matching_rules =
WORKSPACE_MATCHING_RULES.lock();
if let Some(rules) = workspace
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.workspace_rules.as_ref())
{
@@ -672,7 +676,7 @@ where
}
if let Some(rules) = workspace
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.initial_workspace_rules.as_ref())
{
@@ -734,8 +738,8 @@ where
mod tests {
use super::*;
use crate::window_manager_event::WindowManagerEvent;
use crossbeam_channel::bounded;
use crossbeam_channel::Sender;
use crossbeam_channel::bounded;
use std::path::PathBuf;
use uuid::Uuid;
use windows::Win32::Devices::Display::DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
@@ -1006,18 +1010,18 @@ mod tests {
assert_eq!(monitors.len(), 1, "Expected one monitor");
// hmonitor
assert_eq!(monitors[0].id(), 1);
assert_eq!(monitors[0].id, 1);
// device name
assert_eq!(monitors[0].name(), &String::from("DISPLAY1"));
assert_eq!(monitors[0].name, String::from("DISPLAY1"));
// Device
assert_eq!(monitors[0].device(), &String::from("ABC123"));
assert_eq!(monitors[0].device, String::from("ABC123"));
// Device ID
assert_eq!(
monitors[0].device_id(),
&String::from("ABC123-4&123456&0&UID0")
monitors[0].device_id,
String::from("ABC123-4&123456&0&UID0")
);
// Check monitor serial number id

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use color_eyre::eyre;
use color_eyre::eyre::OptionExt;
use crossbeam_utils::atomic::AtomicConsume;
use parking_lot::Mutex;
@@ -11,6 +11,18 @@ use crate::core::Rect;
use crate::core::Sizing;
use crate::core::WindowContainerBehaviour;
use crate::CURRENT_VIRTUAL_DESKTOP;
use crate::DefaultLayout;
use crate::FLOATING_APPLICATIONS;
use crate::HIDDEN_HWNDS;
use crate::Layout;
use crate::Notification;
use crate::NotificationEvent;
use crate::REGEX_IDENTIFIERS;
use crate::State;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::VirtualDesktopNotification;
use crate::Window;
use crate::border_manager;
use crate::border_manager::BORDER_OFFSET;
use crate::border_manager::BORDER_WIDTH;
@@ -18,25 +30,13 @@ use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::stackbar_manager;
use crate::transparency_manager;
use crate::window::should_act;
use crate::window::RuleDebug;
use crate::window::should_act;
use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent::WinEvent;
use crate::workspace::WorkspaceLayer;
use crate::DefaultLayout;
use crate::Layout;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::VirtualDesktopNotification;
use crate::Window;
use crate::CURRENT_VIRTUAL_DESKTOP;
use crate::FLOATING_APPLICATIONS;
use crate::HIDDEN_HWNDS;
use crate::REGEX_IDENTIFIERS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
#[tracing::instrument]
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
@@ -65,7 +65,7 @@ pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
impl WindowManager {
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
#[tracing::instrument(skip(self, event), fields(event = event.title(), winevent = event.winevent(), hwnd = event.hwnd()))]
pub fn process_event(&mut self, event: WindowManagerEvent) -> Result<()> {
pub fn process_event(&mut self, event: WindowManagerEvent) -> eyre::Result<()> {
if self.is_paused {
tracing::trace!("ignoring while paused");
return Ok(());
@@ -145,7 +145,9 @@ impl WindowManager {
// to be consumed by integrating gui applications like bars to know
// when to hide visual components which don't make sense when not on
// komorebi's associated virtual desktop
tracing::debug!("notifying subscribers that we have left komorebi's associated virtual desktop");
tracing::debug!(
"notifying subscribers that we have left komorebi's associated virtual desktop"
);
notify_subscribers(
Notification {
event: NotificationEvent::VirtualDesktop(
@@ -165,7 +167,9 @@ impl WindowManager {
// to be consumed by integrating gui applications like bars to know
// when to show visual components associated with komorebi's virtual
// desktop
tracing::debug!("notifying subscribers that we are back on komorebi's associated virtual desktop");
tracing::debug!(
"notifying subscribers that we are back on komorebi's associated virtual desktop"
);
notify_subscribers(
Notification {
event: NotificationEvent::VirtualDesktop(
@@ -203,12 +207,11 @@ impl WindowManager {
//
// This check ensures that we only update the focused monitor when the window
// triggering monitor reconciliation is known to not be tied to a specific monitor.
if let Ok(class) = window.class() {
if class != "OleMainThreadWndClass"
&& self.focused_monitor_idx() != monitor_idx
{
self.focus_monitor(monitor_idx)?;
}
if let Ok(class) = window.class()
&& class != "OleMainThreadWndClass"
&& self.focused_monitor_idx() != monitor_idx
{
self.focus_monitor(monitor_idx)?;
}
}
}
@@ -304,7 +307,7 @@ impl WindowManager {
// containers - this makes floating windows on empty workspaces go into very
// annoying focus change loops which prevents users from interacting with them
if !matches!(
self.focused_workspace()?.layout(),
self.focused_workspace()?.layout,
Layout::Default(DefaultLayout::Scrolling)
) && !self.focused_workspace()?.containers().is_empty()
{
@@ -319,13 +322,13 @@ impl WindowManager {
match floating_window_idx {
None => {
if let Some(w) = workspace.maximized_window() {
if w.hwnd == window.hwnd {
return Ok(());
}
if let Some(w) = &workspace.maximized_window
&& w.hwnd == window.hwnd
{
return Ok(());
}
if let Some(monocle) = workspace.monocle_container() {
if let Some(monocle) = &workspace.monocle_container {
if let Some(window) = monocle.focused_window() {
window.focus(false)?;
}
@@ -333,10 +336,10 @@ impl WindowManager {
workspace.focus_container_by_window(window.hwnd)?;
}
workspace.set_layer(WorkspaceLayer::Tiling);
workspace.layer = WorkspaceLayer::Tiling;
if matches!(
self.focused_workspace()?.layout(),
self.focused_workspace()?.layout,
Layout::Default(DefaultLayout::Scrolling)
) && !self.focused_workspace()?.containers().is_empty()
{
@@ -345,7 +348,7 @@ impl WindowManager {
}
Some(idx) => {
if let Some(_window) = workspace.floating_windows().get(idx) {
workspace.set_layer(WorkspaceLayer::Floating);
workspace.layer = WorkspaceLayer::Floating;
}
}
}
@@ -389,23 +392,20 @@ impl WindowManager {
}
}
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd) {
if let Some(focused_workspace_idx) = self
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd)
&& let Some(focused_workspace_idx) = self
.monitors()
.get(*m_idx)
.map(|m| m.focused_workspace_idx())
{
if *m_idx != self.focused_monitor_idx()
&& *w_idx != focused_workspace_idx
{
tracing::debug!(
"ignoring show event for window already associated with another workspace"
);
&& *m_idx != self.focused_monitor_idx()
&& *w_idx != focused_workspace_idx
{
tracing::debug!(
"ignoring show event for window already associated with another workspace"
);
window.hide();
proceed = false;
}
}
window.hide();
proceed = false;
}
if proceed {
@@ -415,7 +415,7 @@ impl WindowManager {
);
let workspace = self.focused_workspace_mut()?;
let workspace_contains_window = workspace.contains_window(window.hwnd);
let monocle_container = workspace.monocle_container().clone();
let monocle_container = workspace.monocle_container.clone();
if !workspace_contains_window && needs_reconciliation.is_none() {
let floating_applications = FLOATING_APPLICATIONS.lock();
@@ -458,11 +458,11 @@ impl WindowManager {
let center_spawned_floats =
placement.should_center() && workspace.tile;
workspace.floating_windows_mut().push_back(window);
workspace.set_layer(WorkspaceLayer::Floating);
workspace.layer = WorkspaceLayer::Floating;
if center_spawned_floats {
let mut floating_window = window;
floating_window.center(
&workspace.globals().work_area,
&workspace.globals.work_area,
placement.should_resize(),
)?;
}
@@ -471,17 +471,15 @@ impl WindowManager {
match behaviour.current_behaviour {
WindowContainerBehaviour::Create => {
workspace.new_container_for_window(window);
workspace.set_layer(WorkspaceLayer::Tiling);
workspace.layer = WorkspaceLayer::Tiling;
self.update_focused_workspace(false, false)?;
}
WindowContainerBehaviour::Append => {
workspace
.focused_container_mut()
.ok_or_else(|| {
anyhow!("there is no focused container")
})?
.ok_or_eyre("there is no focused container")?
.add_window(window);
workspace.set_layer(WorkspaceLayer::Tiling);
workspace.layer = WorkspaceLayer::Tiling;
self.update_focused_workspace(true, false)?;
stackbar_manager::send_notification();
}
@@ -506,17 +504,15 @@ impl WindowManager {
if workspace_contains_window {
let mut monocle_window_event = false;
if let Some(ref monocle) = monocle_container {
if let Some(monocle_window) = monocle.focused_window() {
if monocle_window.hwnd == window.hwnd {
monocle_window_event = true;
}
}
if let Some(ref monocle) = monocle_container
&& let Some(monocle_window) = monocle.focused_window()
&& monocle_window.hwnd == window.hwnd
{
monocle_window_event = true;
}
let workspace = self.focused_workspace()?;
if !(monocle_window_event
|| workspace.layer() != &WorkspaceLayer::Tiling)
if !(monocle_window_event || workspace.layer != WorkspaceLayer::Tiling)
&& monocle_container.is_some()
{
window.hide();
@@ -529,7 +525,7 @@ impl WindowManager {
let monitor_idx = self.focused_monitor_idx();
let workspace_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.ok_or_eyre("there is no monitor with this idx")?
.focused_workspace_idx();
WindowsApi::bring_window_to_top(window.hwnd)?;
@@ -547,19 +543,19 @@ impl WindowManager {
// If the window handles don't match then something went wrong and the pending move
// is not related to this current move, if so abort this operation.
if let Some((_, _, w_hwnd)) = pending {
if w_hwnd != window.hwnd {
color_eyre::eyre::bail!(
"window handles for move operation don't match: {} != {}",
w_hwnd,
window.hwnd
);
}
if let Some((_, _, w_hwnd)) = pending
&& w_hwnd != window.hwnd
{
color_eyre::eyre::bail!(
"window handles for move operation don't match: {} != {}",
w_hwnd,
window.hwnd
);
}
let target_monitor_idx = self
.monitor_idx_from_current_pos()
.ok_or_else(|| anyhow!("cannot get monitor idx from current position"))?;
.ok_or_eyre("cannot get monitor idx from current position")?;
let focused_monitor_idx = self.focused_monitor_idx();
let focused_workspace_idx = self.focused_workspace_idx().unwrap_or_default();
@@ -570,7 +566,7 @@ impl WindowManager {
let focused_container_idx = workspace.focused_container_idx();
let new_position = WindowsApi::window_rect(window.hwnd)?;
let old_position = *workspace
.latest_layout()
.latest_layout
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
@@ -582,10 +578,10 @@ impl WindowManager {
// This will be true if we have moved to another monitor
let mut moved_across_monitors = false;
if let Some((m_idx, _)) = self.known_hwnds.get(&window.hwnd) {
if *m_idx != target_monitor_idx {
moved_across_monitors = true;
}
if let Some((m_idx, _)) = self.known_hwnds.get(&window.hwnd)
&& *m_idx != target_monitor_idx
{
moved_across_monitors = true;
}
if let Some((origin_monitor_idx, origin_workspace_idx, _)) = pending {
@@ -603,10 +599,10 @@ impl WindowManager {
let origin_workspace = self
.monitors()
.get(origin_monitor_idx)
.ok_or_else(|| anyhow!("cannot get monitor idx"))?
.ok_or_eyre("cannot get monitor idx")?
.workspaces()
.get(origin_workspace_idx)
.ok_or_else(|| anyhow!("cannot get workspace idx"))?;
.ok_or_eyre("cannot get workspace idx")?;
let managed_window = origin_workspace.contains_window(window.hwnd);
@@ -618,7 +614,7 @@ impl WindowManager {
}
let workspace = self.focused_workspace_mut()?;
if (*workspace.tile() && workspace.contains_managed_window(window.hwnd))
if (workspace.tile && workspace.contains_managed_window(window.hwnd))
|| moved_across_monitors
{
let resize = Rect {
@@ -646,17 +642,15 @@ impl WindowManager {
let target_workspace_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.ok_or_eyre("there is no monitor at this idx")?
.focused_workspace_idx();
let target_container_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.ok_or_eyre("there is no monitor at this idx")?
.focused_workspace()
.ok_or_else(|| {
anyhow!("there is no focused workspace for this monitor")
})?
.ok_or_eyre("there is no focused workspace for this monitor")?
.container_idx_from_current_point()
// Default to 0 in the case of an empty workspace
.unwrap_or(0);
@@ -676,7 +670,7 @@ impl WindowManager {
let origin_monitor = self
.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?;
.ok_or_eyre("there is no monitor at this idx")?;
origin_monitor.focus_workspace(origin_workspace_idx)?;
self.update_focused_workspace(false, false)?;
@@ -684,7 +678,7 @@ impl WindowManager {
let target_monitor = self
.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?;
.ok_or_eyre("there is no monitor at this idx")?;
target_monitor.focus_workspace(target_workspace_idx)?;
self.update_focused_workspace(false, false)?;
@@ -836,7 +830,7 @@ impl WindowManager {
.and_then(|m| m.workspaces().get(*ws_idx))
{
if let Some(monocle_with_window) = target_workspace
.monocle_container()
.monocle_container
.as_ref()
.and_then(|m| m.contains_window(window.hwnd).then_some(m))
{
@@ -849,13 +843,12 @@ impl WindowManager {
if let Some(target_container) =
c_idx.and_then(|c_idx| target_workspace.containers().get(c_idx))
&& target_container.focused_window() != Some(&window)
{
if target_container.focused_window() != Some(&window) {
tracing::debug!(
"Needs reconciliation within a stack on the focused workspace"
);
needs_reconciliation = Some((*m_idx, *ws_idx));
}
tracing::debug!(
"Needs reconciliation within a stack on the focused workspace"
);
needs_reconciliation = Some((*m_idx, *ws_idx));
}
}
}
@@ -886,13 +879,13 @@ impl WindowManager {
if let Some(monitor) = self.focused_monitor_mut() {
if ws_idx != monitor.focused_workspace_idx() {
let previous_idx = monitor.focused_workspace_idx();
monitor.set_last_focused_workspace(Option::from(previous_idx));
monitor.last_focused_workspace = Option::from(previous_idx);
monitor.focus_workspace(ws_idx)?;
}
if let Some(workspace) = monitor.focused_workspace_mut() {
let mut layer = WorkspaceLayer::Tiling;
if let Some((monocle, idx)) = workspace
.monocle_container_mut()
.monocle_container
.as_mut()
.and_then(|m| m.idx_for_window(window.hwnd).map(|i| (m, i)))
{
@@ -903,14 +896,14 @@ impl WindowManager {
.any(|w| w.hwnd == window.hwnd)
{
layer = WorkspaceLayer::Floating;
} else if !workspace
.maximized_window()
.is_some_and(|w| w.hwnd == window.hwnd)
} else if workspace
.maximized_window
.is_none_or(|w| w.hwnd != window.hwnd)
{
// If the window is the maximized window do nothing, else we
// reintegrate the monocle if it exists and then focus the
// container
if workspace.monocle_container().is_some() {
if workspace.monocle_container.is_some() {
tracing::info!("disabling monocle");
for container in workspace.containers_mut() {
container.restore();
@@ -922,7 +915,7 @@ impl WindowManager {
}
workspace.focus_container_by_window(window.hwnd)?;
}
workspace.set_layer(layer);
workspace.layer = layer;
}
monitor.load_focused_workspace(mouse_follows_focus)?;
monitor.update_focused_workspace(offset)?;

View File

@@ -1,9 +1,9 @@
use std::sync::Arc;
use parking_lot::Mutex;
use winput::Action;
use winput::message_loop;
use winput::message_loop::Event;
use winput::Action;
use crate::core::FocusFollowsMouseImplementation;

View File

@@ -1,15 +1,15 @@
#![deny(clippy::unwrap_used, clippy::expect_used)]
use crate::border_manager;
use crate::notify_subscribers;
use crate::winevent::WinEvent;
use crate::DATA_DIR;
use crate::HIDING_BEHAVIOUR;
use crate::HidingBehaviour;
use crate::NotificationEvent;
use crate::Window;
use crate::WindowManager;
use crate::WindowManagerEvent;
use crate::DATA_DIR;
use crate::HIDING_BEHAVIOUR;
use crate::border_manager;
use crate::notify_subscribers;
use crate::winevent::WinEvent;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
@@ -55,13 +55,15 @@ pub fn listen_for_notifications(
) {
watch_for_orphans(known_hwnds);
std::thread::spawn(move || loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});
@@ -150,16 +152,18 @@ fn watch_for_orphans(known_hwnds: HashMap<isize, (usize, usize)>) {
*cache = known_hwnds;
}
std::thread::spawn(move || loop {
match find_orphans() {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
if cfg!(debug_assertions) {
tracing::error!("restarting failed thread: {:?}", error)
} else {
tracing::error!("restarting failed thread: {}", error)
std::thread::spawn(move || {
loop {
match find_orphans() {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
if cfg!(debug_assertions) {
tracing::error!("restarting failed thread: {:?}", error)
} else {
tracing::error!("restarting failed thread: {}", error)
}
}
}
}

View File

@@ -1,26 +1,26 @@
mod stackbar;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::WindowManager;
use crate::WindowsApi;
use crate::container::Container;
use crate::core::StackbarLabel;
use crate::core::StackbarMode;
use crate::stackbar_manager::stackbar::Stackbar;
use crate::WindowManager;
use crate::WindowsApi;
use crate::DEFAULT_CONTAINER_PADDING;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicCell;
use crossbeam_utils::atomic::AtomicConsume;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::OnceLock;
pub static STACKBAR_FONT_SIZE: AtomicI32 = AtomicI32::new(0); // 0 will produce the system default
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
@@ -71,13 +71,15 @@ pub fn should_have_stackbar(window_count: usize) -> bool {
}
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
std::thread::spawn(move || loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});
@@ -111,7 +113,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
// Only operate on the focused workspace of each monitor
if let Some(ws) = m.focused_workspace_mut() {
// Workspaces with tiling disabled don't have stackbars
if !ws.tile() {
if !ws.tile {
let mut to_remove = vec![];
for (id, border) in stackbars.iter() {
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx {
@@ -131,7 +133,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
WindowsApi::is_zoomed(WindowsApi::foreground_window().unwrap_or_default());
// Handle the monocle container separately
if ws.monocle_container().is_some() || is_maximized {
if ws.monocle_container.is_some() || is_maximized {
// Destroy any stackbars associated with the focused workspace
let mut to_remove = vec![];
for (id, stackbar) in stackbars.iter() {
@@ -152,7 +154,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
let container_ids = ws
.containers()
.iter()
.map(|c| c.id().clone())
.map(|c| c.id.clone())
.collect::<Vec<_>>();
let mut to_remove = vec![];
@@ -170,7 +172,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
}
let container_padding = ws
.container_padding()
.container_padding
.unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume());
'containers: for container in ws.containers_mut() {
@@ -181,20 +183,20 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
};
if !should_add_stackbar {
if let Some(stackbar) = stackbars.get(container.id()) {
if let Some(stackbar) = stackbars.get(&container.id) {
stackbar.destroy()?
}
stackbars.remove(container.id());
stackbars_monitors.remove(container.id());
stackbars.remove(&container.id);
stackbars_monitors.remove(&container.id);
continue 'containers;
}
// Get the stackbar entry for this container from the map or create one
let stackbar = match stackbars.entry(container.id().clone()) {
let stackbar = match stackbars.entry(container.id.clone()) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => {
if let Ok(stackbar) = Stackbar::create(container.id()) {
if let Ok(stackbar) = Stackbar::create(&container.id) {
entry.insert(stackbar)
} else {
continue 'receiver;
@@ -202,7 +204,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
}
};
stackbars_monitors.insert(container.id().clone(), monitor_idx);
stackbars_monitors.insert(container.id.clone(), monitor_idx);
let rect = WindowsApi::window_rect(
container.focused_window().copied().unwrap_or_default().hwnd,

View File

@@ -1,3 +1,6 @@
use crate::DEFAULT_CONTAINER_PADDING;
use crate::WINDOWS_11;
use crate::WindowsApi;
use crate::border_manager::BORDER_OFFSET;
use crate::border_manager::BORDER_WIDTH;
use crate::border_manager::STYLE;
@@ -5,7 +8,6 @@ use crate::container::Container;
use crate::core::BorderStyle;
use crate::core::Rect;
use crate::core::StackbarLabel;
use crate::stackbar_manager::STACKBARS_CONTAINERS;
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::stackbar_manager::STACKBAR_FONT_FAMILY;
use crate::stackbar_manager::STACKBAR_FONT_SIZE;
@@ -14,16 +16,13 @@ use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
use crate::stackbar_manager::STACKBAR_TAB_HEIGHT;
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
use crate::stackbar_manager::STACKBARS_CONTAINERS;
use crate::windows_api;
use crate::WindowsApi;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::WINDOWS_11;
use crossbeam_utils::atomic::AtomicConsume;
use std::os::windows::ffi::OsStrExt;
use std::sync::atomic::Ordering;
use std::sync::mpsc;
use std::time::Duration;
use windows::core::PCWSTR;
use windows::Win32::Foundation::COLORREF;
use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::Foundation::HWND;
@@ -33,41 +32,41 @@ use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
use windows::Win32::Graphics::Gdi::DT_CENTER;
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
use windows::Win32::Graphics::Gdi::DT_VCENTER;
use windows::Win32::Graphics::Gdi::DeleteObject;
use windows::Win32::Graphics::Gdi::DrawTextW;
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
use windows::Win32::Graphics::Gdi::FW_BOLD;
use windows::Win32::Graphics::Gdi::GetDC;
use windows::Win32::Graphics::Gdi::GetDeviceCaps;
use windows::Win32::Graphics::Gdi::LOGFONTW;
use windows::Win32::Graphics::Gdi::LOGPIXELSY;
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::ReleaseDC;
use windows::Win32::Graphics::Gdi::RoundRect;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::SetBkColor;
use windows::Win32::Graphics::Gdi::SetTextColor;
use windows::Win32::Graphics::Gdi::DT_CENTER;
use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS;
use windows::Win32::Graphics::Gdi::DT_SINGLELINE;
use windows::Win32::Graphics::Gdi::DT_VCENTER;
use windows::Win32::Graphics::Gdi::FONT_QUALITY;
use windows::Win32::Graphics::Gdi::FW_BOLD;
use windows::Win32::Graphics::Gdi::LOGFONTW;
use windows::Win32::Graphics::Gdi::LOGPIXELSY;
use windows::Win32::Graphics::Gdi::PROOF_QUALITY;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::System::WindowsProgramming::MulDiv;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::IDC_ARROW;
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
use windows::Win32::UI::WindowsAndMessaging::LoadCursorW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::SetCursor;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::IDC_ARROW;
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
use windows::Win32::UI::WindowsAndMessaging::WM_SETCURSOR;
@@ -76,6 +75,7 @@ use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
use windows::core::PCWSTR;
#[derive(Debug)]
pub struct Stackbar {
@@ -353,16 +353,15 @@ impl Stackbar {
// stackbar, make sure we update its location so that it doesn't render
// on top of other tiles before eventually ending up in the correct
// tile
if index != focused_window_idx {
if let Err(err) =
if index != focused_window_idx
&& let Err(err) =
window.set_position(&focused_window_rect, false)
{
tracing::error!(
{
tracing::error!(
"stackbar WM_LBUTTONDOWN repositioning error: hwnd {} ({})",
*window,
err
);
}
}
// Restore the window corresponding to the tab we have clicked

View File

@@ -1,4 +1,33 @@
use crate::animation::PerAnimationPrefixConfig;
use crate::AspectRatio;
use crate::Axis;
use crate::CrossBoundaryBehaviour;
use crate::DATA_DIR;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::FLOATING_APPLICATIONS;
use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO;
use crate::FloatingLayerBehaviour;
use crate::HIDING_BEHAVIOUR;
use crate::IGNORE_IDENTIFIERS;
use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::NO_TITLEBAR;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST;
use crate::Placement;
use crate::PredefinedAspectRatio;
use crate::REGEX_IDENTIFIERS;
use crate::ResolvedPathBuf;
use crate::SLOW_APPLICATION_COMPENSATION_TIME;
use crate::SLOW_APPLICATION_IDENTIFIERS;
use crate::TRANSPARENCY_BLACKLIST;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WINDOW_HANDLING_BEHAVIOUR;
use crate::WINDOWS_11;
use crate::WORKSPACE_MATCHING_RULES;
use crate::WindowHandlingBehaviour;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
@@ -7,18 +36,14 @@ use crate::animation::ANIMATION_FPS;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::animation::DEFAULT_ANIMATION_FPS;
use crate::animation::PerAnimationPrefixConfig;
use crate::asc::ApplicationSpecificConfiguration;
use crate::asc::AscApplicationRulesOrSchema;
use crate::border_manager;
use crate::border_manager::ZOrder;
use crate::border_manager::IMPLEMENTATION;
use crate::border_manager::STYLE;
use crate::border_manager::ZOrder;
use crate::config_generation::WorkspaceMatchingRule;
use crate::core::config_generation::ApplicationConfiguration;
use crate::core::config_generation::ApplicationConfigurationGenerator;
use crate::core::config_generation::ApplicationOptions;
use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy;
use crate::core::AnimationStyle;
use crate::core::BorderImplementation;
use crate::core::BorderStyle;
@@ -34,6 +59,11 @@ use crate::core::StackbarLabel;
use crate::core::StackbarMode;
use crate::core::WindowContainerBehaviour;
use crate::core::WindowManagementBehaviour;
use crate::core::config_generation::ApplicationConfiguration;
use crate::core::config_generation::ApplicationConfigurationGenerator;
use crate::core::config_generation::ApplicationOptions;
use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy;
use crate::current_virtual_desktop;
use crate::default_layout::LayoutOptions;
use crate::monitor;
@@ -57,37 +87,7 @@ use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::workspace::Workspace;
use crate::AspectRatio;
use crate::Axis;
use crate::CrossBoundaryBehaviour;
use crate::FloatingLayerBehaviour;
use crate::Placement;
use crate::PredefinedAspectRatio;
use crate::ResolvedPathBuf;
use crate::WindowHandlingBehaviour;
use crate::DATA_DIR;
use crate::DEFAULT_CONTAINER_PADDING;
use crate::DEFAULT_WORKSPACE_PADDING;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::FLOATING_APPLICATIONS;
use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO;
use crate::HIDING_BEHAVIOUR;
use crate::IGNORE_IDENTIFIERS;
use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::NO_TITLEBAR;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST;
use crate::REGEX_IDENTIFIERS;
use crate::SLOW_APPLICATION_COMPENSATION_TIME;
use crate::SLOW_APPLICATION_IDENTIFIERS;
use crate::TRANSPARENCY_BLACKLIST;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WINDOWS_11;
use crate::WINDOW_HANDLING_BEHAVIOUR;
use crate::WORKSPACE_MATCHING_RULES;
use color_eyre::Result;
use color_eyre::eyre;
use crossbeam_channel::Receiver;
use hotwatch::EventKind;
use hotwatch::Hotwatch;
@@ -101,8 +101,8 @@ use std::collections::HashSet;
use std::io::ErrorKind;
use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use uds_windows::UnixListener;
use uds_windows::UnixStream;
@@ -250,7 +250,7 @@ pub struct WorkspaceConfig {
impl From<&Workspace> for WorkspaceConfig {
fn from(value: &Workspace) -> Self {
let mut layout_rules = HashMap::new();
for (threshold, layout) in value.layout_rules() {
for (threshold, layout) in &value.layout_rules {
match layout {
Layout::Default(value) => {
layout_rules.insert(*threshold, *value);
@@ -261,14 +261,14 @@ impl From<&Workspace> for WorkspaceConfig {
let layout_rules = (!layout_rules.is_empty()).then_some(layout_rules);
let mut window_container_behaviour_rules = HashMap::new();
for (threshold, behaviour) in value.window_container_behaviour_rules().iter().flatten() {
for (threshold, behaviour) in value.window_container_behaviour_rules.iter().flatten() {
window_container_behaviour_rules.insert(*threshold, *behaviour);
}
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
let default_workspace_padding = DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst);
let container_padding = value.container_padding().and_then(|container_padding| {
let container_padding = value.container_padding.and_then(|container_padding| {
if container_padding == default_container_padding {
None
} else {
@@ -276,7 +276,7 @@ impl From<&Workspace> for WorkspaceConfig {
}
});
let workspace_padding = value.workspace_padding().and_then(|workspace_padding| {
let workspace_padding = value.workspace_padding.and_then(|workspace_padding| {
if workspace_padding == default_workspace_padding {
None
} else {
@@ -284,48 +284,48 @@ impl From<&Workspace> for WorkspaceConfig {
}
});
let tile = if *value.tile() { None } else { Some(false) };
let tile = if value.tile { None } else { Some(false) };
Self {
name: value
.name()
.name
.clone()
.unwrap_or_else(|| String::from("unnamed")),
layout: value
.tile()
.then_some(match value.layout() {
Layout::Default(layout) => Option::from(*layout),
.tile
.then_some(match value.layout {
Layout::Default(layout) => Option::from(layout),
Layout::Custom(_) => None,
})
.flatten(),
layout_options: value.layout_options(),
layout_options: value.layout_options,
custom_layout: value
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.custom_layout.clone()),
layout_rules,
custom_layout_rules: value
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.custom_layout_rules.clone()),
container_padding,
workspace_padding,
initial_workspace_rules: value
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.initial_workspace_rules.clone()),
workspace_rules: value
.workspace_config()
.workspace_config
.as_ref()
.and_then(|c| c.workspace_rules.clone()),
work_area_offset: value.work_area_offset(),
apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()),
window_container_behaviour: *value.window_container_behaviour(),
work_area_offset: value.work_area_offset,
apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset),
window_container_behaviour: value.window_container_behaviour,
window_container_behaviour_rules: Option::from(window_container_behaviour_rules),
float_override: *value.float_override(),
float_override: value.float_override,
tile,
layout_flip: value.layout_flip(),
floating_layer_behaviour: value.floating_layer_behaviour(),
layout_flip: value.layout_flip,
floating_layer_behaviour: value.floating_layer_behaviour,
wallpaper: None,
}
}
@@ -369,7 +369,7 @@ impl From<&Monitor> for MonitorConfig {
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
let default_workspace_padding = DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst);
let container_padding = value.container_padding().and_then(|container_padding| {
let container_padding = value.container_padding.and_then(|container_padding| {
if container_padding == default_container_padding {
None
} else {
@@ -377,7 +377,7 @@ impl From<&Monitor> for MonitorConfig {
}
});
let workspace_padding = value.workspace_padding().and_then(|workspace_padding| {
let workspace_padding = value.workspace_padding.and_then(|workspace_padding| {
if workspace_padding == default_workspace_padding {
None
} else {
@@ -387,13 +387,13 @@ impl From<&Monitor> for MonitorConfig {
Self {
workspaces,
work_area_offset: value.work_area_offset(),
window_based_work_area_offset: value.window_based_work_area_offset(),
window_based_work_area_offset_limit: Some(value.window_based_work_area_offset_limit()),
work_area_offset: value.work_area_offset,
window_based_work_area_offset: value.window_based_work_area_offset,
window_based_work_area_offset_limit: Some(value.window_based_work_area_offset_limit),
container_padding,
workspace_padding,
wallpaper: value.wallpaper().clone(),
floating_layer_behaviour: value.floating_layer_behaviour(),
wallpaper: value.wallpaper.clone(),
floating_layer_behaviour: value.floating_layer_behaviour,
}
}
}
@@ -412,7 +412,7 @@ pub enum AppSpecificConfigurationPath {
#[serde_with::serde_as]
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
/// The `komorebi.json` static configuration file reference for `v0.1.38`
/// The `komorebi.json` static configuration file reference for `v0.1.39`
pub struct StaticConfig {
/// DEPRECATED from v0.1.22: no longer required
#[serde(skip_serializing_if = "Option::is_none")]
@@ -731,7 +731,9 @@ impl StaticConfig {
}
if display {
println!("\nEnd-of-life features will not receive any further bug fixes or updates; they should not be used\n")
println!(
"\nEnd-of-life features will not receive any further bug fixes or updates; they should not be used\n"
)
}
}
@@ -756,7 +758,9 @@ impl StaticConfig {
}
if display {
println!("\nYour configuration file contains some options that have been renamed or deprecated:\n");
println!(
"\nYour configuration file contains some options that have been renamed or deprecated:\n"
);
for (canonical, aliases) in map {
for alias in aliases {
if raw.contains(alias) {
@@ -948,7 +952,7 @@ impl From<&WindowManager> for StaticConfig {
impl StaticConfig {
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
fn apply_globals(&mut self) -> Result<()> {
fn apply_globals(&mut self) -> eyre::Result<()> {
*FLOATING_WINDOW_TOGGLE_ASPECT_RATIO.lock() = self
.floating_window_aspect_ratio
.unwrap_or(AspectRatio::Predefined(PredefinedAspectRatio::Standard));
@@ -1236,11 +1240,11 @@ impl StaticConfig {
Ok(())
}
pub fn read_raw(raw: &str) -> Result<Self> {
pub fn read_raw(raw: &str) -> eyre::Result<Self> {
Ok(serde_json::from_str(raw)?)
}
pub fn read(path: &PathBuf) -> Result<Self> {
pub fn read(path: &PathBuf) -> eyre::Result<Self> {
let content = std::fs::read_to_string(path)?;
serde_json::from_str(&content).map_err(Into::into)
}
@@ -1250,7 +1254,7 @@ impl StaticConfig {
path: &PathBuf,
incoming: Receiver<WindowManagerEvent>,
unix_listener: Option<UnixListener>,
) -> Result<WindowManager> {
) -> eyre::Result<WindowManager> {
let mut value = Self::read(path)?;
value.apply_globals()?;
@@ -1345,7 +1349,7 @@ impl StaticConfig {
Ok(wm)
}
pub fn postload(path: &PathBuf, wm: &Arc<Mutex<WindowManager>>) -> Result<()> {
pub fn postload(path: &PathBuf, wm: &Arc<Mutex<WindowManager>>) -> eyre::Result<()> {
let mut value = Self::read(path)?;
let mut wm = wm.lock();
@@ -1363,15 +1367,12 @@ impl StaticConfig {
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
let preferred_config_idx = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
(monitor
.serial_number_id()
.as_ref()
.is_some_and(|sn| sn == id)
|| monitor.device_id() == id)
.then_some(*c_idx)
});
c_idx
display_index_preferences.iter().find_map(|(c_idx, id)| {
(monitor.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|| monitor.device_id.eq(id))
.then_some(*c_idx)
})
};
let idx = preferred_config_idx.or({
// Monitor without preferred config idx.
@@ -1396,19 +1397,16 @@ impl StaticConfig {
}
monitor.ensure_workspace_count(monitor_config.workspaces.len());
monitor.set_work_area_offset(monitor_config.work_area_offset);
monitor.set_window_based_work_area_offset(
monitor_config.window_based_work_area_offset,
);
monitor.set_window_based_work_area_offset_limit(
monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1),
);
monitor.set_container_padding(monitor_config.container_padding);
monitor.set_workspace_padding(monitor_config.workspace_padding);
monitor.set_wallpaper(monitor_config.wallpaper.clone());
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
monitor.work_area_offset = monitor_config.work_area_offset;
monitor.window_based_work_area_offset =
monitor_config.window_based_work_area_offset;
monitor.window_based_work_area_offset_limit = monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1);
monitor.container_padding = monitor_config.container_padding;
monitor.workspace_padding = monitor_config.workspace_padding;
monitor.wallpaper = monitor_config.wallpaper.clone();
monitor.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
monitor.update_workspaces_globals(offset);
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
@@ -1416,7 +1414,9 @@ impl StaticConfig {
if monitor_count > 1
&& matches!(workspace_config.layout, Some(DefaultLayout::Scrolling))
{
tracing::warn!("scrolling layout is only supported for a single monitor; falling back to columns layout");
tracing::warn!(
"scrolling layout is only supported for a single monitor; falling back to columns layout"
);
workspace_config.layout = Some(DefaultLayout::Columns);
}
@@ -1428,9 +1428,9 @@ impl StaticConfig {
// a copy of the monitor itself on the monitor cache if it is.
if idx == preferred_config_idx {
let id = monitor
.serial_number_id()
.serial_number_id
.as_ref()
.map_or(monitor.device_id(), |sn| sn);
.map_or(&monitor.device_id, |sn| sn);
monitor_reconciliator::insert_in_monitor_cache(id, monitor.clone());
}
@@ -1490,18 +1490,14 @@ impl StaticConfig {
);
m.ensure_workspace_count(monitor_config.workspaces.len());
m.set_work_area_offset(monitor_config.work_area_offset);
m.set_window_based_work_area_offset(
monitor_config.window_based_work_area_offset,
);
m.set_window_based_work_area_offset_limit(
monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1),
);
m.set_container_padding(monitor_config.container_padding);
m.set_workspace_padding(monitor_config.workspace_padding);
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
m.work_area_offset = monitor_config.work_area_offset;
m.window_based_work_area_offset = monitor_config.window_based_work_area_offset;
m.window_based_work_area_offset_limit = monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1);
m.container_padding = monitor_config.container_padding;
m.workspace_padding = monitor_config.workspace_padding;
m.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
m.update_workspaces_globals(offset);
@@ -1525,7 +1521,7 @@ impl StaticConfig {
Ok(())
}
pub fn reload(path: &PathBuf, wm: &mut WindowManager) -> Result<()> {
pub fn reload(path: &PathBuf, wm: &mut WindowManager) -> eyre::Result<()> {
let mut value = Self::read(path)?;
value.apply_globals()?;
@@ -1542,15 +1538,12 @@ impl StaticConfig {
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
let preferred_config_idx = {
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
(monitor
.serial_number_id()
.as_ref()
.is_some_and(|sn| sn == id)
|| monitor.device_id() == id)
.then_some(*c_idx)
});
c_idx
display_index_preferences.iter().find_map(|(c_idx, id)| {
(monitor.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|| monitor.device_id.eq(id))
.then_some(*c_idx)
})
};
let idx = preferred_config_idx.or({
// Monitor without preferred config idx.
@@ -1575,21 +1568,18 @@ impl StaticConfig {
}
monitor.ensure_workspace_count(monitor_config.workspaces.len());
if monitor.work_area_offset().is_none() {
monitor.set_work_area_offset(monitor_config.work_area_offset);
if monitor.work_area_offset.is_none() {
monitor.work_area_offset = monitor_config.work_area_offset;
}
monitor.set_window_based_work_area_offset(
monitor_config.window_based_work_area_offset,
);
monitor.set_window_based_work_area_offset_limit(
monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1),
);
monitor.set_container_padding(monitor_config.container_padding);
monitor.set_workspace_padding(monitor_config.workspace_padding);
monitor.set_wallpaper(monitor_config.wallpaper.clone());
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
monitor.window_based_work_area_offset =
monitor_config.window_based_work_area_offset;
monitor.window_based_work_area_offset_limit = monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1);
monitor.container_padding = monitor_config.container_padding;
monitor.workspace_padding = monitor_config.workspace_padding;
monitor.wallpaper = monitor_config.wallpaper.clone();
monitor.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
monitor.update_workspaces_globals(offset);
@@ -1603,9 +1593,9 @@ impl StaticConfig {
// a copy of the monitor itself on the monitor cache if it is.
if idx == preferred_config_idx {
let id = monitor
.serial_number_id()
.serial_number_id
.as_ref()
.map_or(monitor.device_id(), |sn| sn);
.map_or(&monitor.device_id, |sn| sn);
monitor_reconciliator::insert_in_monitor_cache(id, monitor.clone());
}
@@ -1665,18 +1655,14 @@ impl StaticConfig {
);
m.ensure_workspace_count(monitor_config.workspaces.len());
m.set_work_area_offset(monitor_config.work_area_offset);
m.set_window_based_work_area_offset(
monitor_config.window_based_work_area_offset,
);
m.set_window_based_work_area_offset_limit(
monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1),
);
m.set_container_padding(monitor_config.container_padding);
m.set_workspace_padding(monitor_config.workspace_padding);
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
m.work_area_offset = monitor_config.work_area_offset;
m.window_based_work_area_offset = monitor_config.window_based_work_area_offset;
m.window_based_work_area_offset_limit = monitor_config
.window_based_work_area_offset_limit
.unwrap_or(1);
m.container_padding = monitor_config.container_padding;
m.workspace_padding = monitor_config.workspace_padding;
m.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
m.update_workspaces_globals(offset);
@@ -1742,7 +1728,7 @@ fn populate_option(
entry: &mut ApplicationConfiguration,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
) -> eyre::Result<()> {
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
@@ -1768,7 +1754,7 @@ fn populate_rules(
matching_rules: &mut Vec<MatchingRule>,
identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
) -> eyre::Result<()> {
for matching_rule in matching_rules {
if !identifiers.contains(matching_rule) {
match matching_rule {
@@ -1814,7 +1800,7 @@ fn handle_asc_file(
transparency_blacklist: &mut Vec<MatchingRule>,
slow_application_identifiers: &mut Vec<MatchingRule>,
regex_identifiers: &mut HashMap<String, Regex>,
) -> Result<()> {
) -> eyre::Result<()> {
match path.extension() {
None => {}
Some(ext) => match ext.to_string_lossy().to_string().as_str() {
@@ -1968,7 +1954,9 @@ mod tests {
#[test]
fn deserialize_custom_layout_rules() {
// set an environment variable for testing
std::env::set_var("VAR", "VALUE");
unsafe {
std::env::set_var("VAR", "VALUE");
}
let config = r#"
{

View File

@@ -1,19 +1,19 @@
#![deny(clippy::unwrap_used, clippy::expect_used)]
use crate::KomorebiTheme;
use crate::border_manager;
use crate::stackbar_manager;
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
use crate::KomorebiTheme;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicCell;
use komorebi_themes::colour::Colour;
use komorebi_themes::Base16Wrapper;
use komorebi_themes::colour::Colour;
use std::ops::Deref;
use std::sync::atomic::Ordering;
use std::sync::OnceLock;
use std::sync::atomic::Ordering;
pub struct Notification(KomorebiTheme);
@@ -51,13 +51,15 @@ pub fn send_notification(theme: KomorebiTheme) {
}
pub fn listen_for_notifications() {
std::thread::spawn(move || loop {
match handle_notifications() {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications() {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});

View File

@@ -4,17 +4,17 @@ use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::atomic::AtomicConsume;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU8;
use std::sync::Arc;
use std::sync::OnceLock;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU8;
use crate::should_act;
use crate::REGEX_IDENTIFIERS;
use crate::TRANSPARENCY_BLACKLIST;
use crate::Window;
use crate::WindowManager;
use crate::WindowsApi;
use crate::REGEX_IDENTIFIERS;
use crate::TRANSPARENCY_BLACKLIST;
use crate::should_act;
pub static TRANSPARENCY_ENABLED: AtomicBool = AtomicBool::new(false);
pub static TRANSPARENCY_ALPHA: AtomicU8 = AtomicU8::new(200);
@@ -49,13 +49,15 @@ pub fn send_notification() {
}
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
std::thread::spawn(move || loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
std::thread::spawn(move || {
loop {
match handle_notifications(wm.clone()) {
Ok(()) => {
tracing::warn!("restarting finished thread");
}
Err(error) => {
tracing::warn!("restarting failed thread: {}", error);
}
}
}
});
@@ -92,7 +94,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
'workspaces: for (workspace_idx, ws) in m.workspaces().iter().enumerate() {
// Only operate on the focused workspace of each monitor
// Workspaces with tiling disabled don't have transparent windows
if !ws.tile() || workspace_idx != focused_workspace_idx {
if !ws.tile || workspace_idx != focused_workspace_idx {
for window in ws.visible_windows().iter().flatten() {
if let Err(error) = window.opaque() {
let hwnd = window.hwnd;
@@ -104,7 +106,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
}
// Monocle container is never transparent
if let Some(monocle) = ws.monocle_container() {
if let Some(monocle) = &ws.monocle_container {
if let Some(window) = monocle.focused_window() {
if monitor_idx == focused_monitor_idx {
if let Err(error) = window.opaque() {
@@ -150,32 +152,34 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
for (window_idx, window) in c.windows().iter().enumerate() {
if window_idx == focused_window_idx {
let mut should_make_transparent = true;
if !transparency_blacklist.is_empty() {
if let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (
if !transparency_blacklist.is_empty()
&& let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (
window.title(),
window.exe(),
window.class(),
window.path(),
) {
let is_blacklisted = should_act(
&title,
&exe_name,
&class,
&path,
&transparency_blacklist,
&regex_identifiers,
)
.is_some();
)
{
let is_blacklisted = should_act(
&title,
&exe_name,
&class,
&path,
&transparency_blacklist,
&regex_identifiers,
)
.is_some();
should_make_transparent = !is_blacklisted;
}
should_make_transparent = !is_blacklisted;
}
if should_make_transparent {
match window.transparent() {
Err(error) => {
let hwnd = foreground_hwnd;
tracing::error!("failed to make unfocused window {hwnd} transparent: {error}" )
tracing::error!(
"failed to make unfocused window {hwnd} transparent: {error}"
)
}
Ok(..) => {
known_hwnds.lock().push(window.hwnd);

View File

@@ -1,31 +1,3 @@
use crate::animation::lerp::Lerp;
use crate::animation::prefix::new_animation_key;
use crate::animation::prefix::AnimationPrefix;
use crate::animation::AnimationEngine;
use crate::animation::RenderDispatcher;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
use crate::animation::ANIMATION_MANAGER;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::border_manager;
use crate::com::SetCloak;
use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy;
use crate::core::ApplicationIdentifier;
use crate::core::HidingBehaviour;
use crate::core::Rect;
use crate::focus_manager;
use crate::stackbar_manager;
use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::transparency_manager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api;
use crate::windows_api::WindowsApi;
use crate::AnimationStyle;
use crate::FLOATING_APPLICATIONS;
use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO;
@@ -40,14 +12,41 @@ use crate::REGEX_IDENTIFIERS;
use crate::SLOW_APPLICATION_COMPENSATION_TIME;
use crate::SLOW_APPLICATION_IDENTIFIERS;
use crate::WSL2_UI_PROCESSES;
use crate::animation::ANIMATION_DURATION_GLOBAL;
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
use crate::animation::ANIMATION_ENABLED_GLOBAL;
use crate::animation::ANIMATION_ENABLED_PER_ANIMATION;
use crate::animation::ANIMATION_MANAGER;
use crate::animation::ANIMATION_STYLE_GLOBAL;
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
use crate::animation::AnimationEngine;
use crate::animation::RenderDispatcher;
use crate::animation::lerp::Lerp;
use crate::animation::prefix::AnimationPrefix;
use crate::animation::prefix::new_animation_key;
use crate::border_manager;
use crate::com::SetCloak;
use crate::core::ApplicationIdentifier;
use crate::core::HidingBehaviour;
use crate::core::Rect;
use crate::core::config_generation::IdWithIdentifier;
use crate::core::config_generation::MatchingRule;
use crate::core::config_generation::MatchingStrategy;
use crate::focus_manager;
use crate::stackbar_manager;
use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::transparency_manager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api;
use crate::windows_api::WindowsApi;
use color_eyre::eyre;
use color_eyre::Result;
use crossbeam_utils::atomic::AtomicConsume;
use regex::Regex;
use serde::ser::SerializeStruct;
use serde::Deserialize;
use serde::Serialize;
use serde::Serializer;
use serde::ser::SerializeStruct;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::Display;
@@ -128,7 +127,7 @@ impl Display for Window {
}
impl Serialize for Window {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
fn serialize<S>(&self, serializer: S) -> eyre::Result<S::Ok, S::Error>
where
S: Serializer,
{
@@ -193,14 +192,14 @@ impl RenderDispatcher for MovementRenderDispatcher {
new_animation_key(MovementRenderDispatcher::PREFIX, self.hwnd.to_string())
}
fn pre_render(&self) -> Result<()> {
fn pre_render(&self) -> eyre::Result<()> {
stackbar_manager::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
stackbar_manager::send_notification();
Ok(())
}
fn render(&self, progress: f64) -> Result<()> {
fn render(&self, progress: f64) -> eyre::Result<()> {
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
// we don't check WINDOW_HANDLING_BEHAVIOUR here because animations
@@ -211,7 +210,7 @@ impl RenderDispatcher for MovementRenderDispatcher {
Ok(())
}
fn post_render(&self) -> Result<()> {
fn post_render(&self) -> eyre::Result<()> {
// we don't add the async_window_pos flag here because animations
// are always run on a separate thread
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, false)?;
@@ -267,7 +266,7 @@ impl RenderDispatcher for TransparencyRenderDispatcher {
new_animation_key(TransparencyRenderDispatcher::PREFIX, self.hwnd.to_string())
}
fn pre_render(&self) -> Result<()> {
fn pre_render(&self) -> eyre::Result<()> {
//transparent
if !self.is_opaque {
let window = Window::from(self.hwnd);
@@ -279,7 +278,7 @@ impl RenderDispatcher for TransparencyRenderDispatcher {
Ok(())
}
fn render(&self, progress: f64) -> Result<()> {
fn render(&self, progress: f64) -> eyre::Result<()> {
WindowsApi::set_transparent(
self.hwnd,
self.start_opacity
@@ -287,7 +286,7 @@ impl RenderDispatcher for TransparencyRenderDispatcher {
)
}
fn post_render(&self) -> Result<()> {
fn post_render(&self) -> eyre::Result<()> {
//opaque
if self.is_opaque {
let window = Window::from(self.hwnd);
@@ -346,7 +345,7 @@ impl Window {
HWND(windows_api::as_ptr!(self.hwnd))
}
pub fn move_to_area(&mut self, current_area: &Rect, target_area: &Rect) -> Result<()> {
pub fn move_to_area(&mut self, current_area: &Rect, target_area: &Rect) -> eyre::Result<()> {
let current_rect = WindowsApi::window_rect(self.hwnd)?;
let x_diff = target_area.left - current_area.left;
let y_diff = target_area.top - current_area.top;
@@ -413,7 +412,7 @@ impl Window {
Ok(())
}
pub fn center(&mut self, work_area: &Rect, resize: bool) -> Result<()> {
pub fn center(&mut self, work_area: &Rect, resize: bool) -> eyre::Result<()> {
let (target_width, target_height) = if resize {
let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO
.lock()
@@ -440,7 +439,7 @@ impl Window {
)
}
pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> {
pub fn set_position(&self, layout: &Rect, top: bool) -> eyre::Result<()> {
let window_rect = WindowsApi::window_rect(self.hwnd)?;
if window_rect.eq(layout) {
@@ -538,7 +537,7 @@ impl Window {
}
}
pub fn close(self) -> Result<()> {
pub fn close(self) -> eyre::Result<()> {
WindowsApi::close_window(self.hwnd)
}
@@ -566,17 +565,17 @@ impl Window {
WindowsApi::unmaximize_window(self.hwnd);
}
pub fn focus(self, mouse_follows_focus: bool) -> Result<()> {
pub fn focus(self, mouse_follows_focus: bool) -> eyre::Result<()> {
// If the target window is already focused, do nothing.
if let Ok(ihwnd) = WindowsApi::foreground_window() {
if ihwnd == self.hwnd {
// Center cursor in Window
if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd)?)?;
}
return Ok(());
if let Ok(ihwnd) = WindowsApi::foreground_window()
&& ihwnd == self.hwnd
{
// Center cursor in Window
if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd)?)?;
}
return Ok(());
}
WindowsApi::raise_and_focus_window(self.hwnd)?;
@@ -593,7 +592,7 @@ impl Window {
WindowsApi::foreground_window().unwrap_or_default() == self.hwnd
}
pub fn transparent(self) -> Result<()> {
pub fn transparent(self) -> eyre::Result<()> {
let animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
let transparent_enabled = animation_enabled.get(&TransparencyRenderDispatcher::PREFIX);
@@ -631,7 +630,7 @@ impl Window {
}
}
pub fn opaque(self) -> Result<()> {
pub fn opaque(self) -> eyre::Result<()> {
let animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
let transparent_enabled = animation_enabled.get(&TransparencyRenderDispatcher::PREFIX);
@@ -666,49 +665,49 @@ impl Window {
}
}
pub fn set_accent(self, colour: u32) -> Result<()> {
pub fn set_accent(self, colour: u32) -> eyre::Result<()> {
WindowsApi::set_window_accent(self.hwnd, Some(colour))
}
pub fn remove_accent(self) -> Result<()> {
pub fn remove_accent(self) -> eyre::Result<()> {
WindowsApi::set_window_accent(self.hwnd, None)
}
#[cfg(target_pointer_width = "64")]
pub fn update_style(self, style: &WindowStyle) -> Result<()> {
pub fn update_style(self, style: &WindowStyle) -> eyre::Result<()> {
WindowsApi::update_style(self.hwnd, isize::try_from(style.bits())?)
}
#[cfg(target_pointer_width = "32")]
pub fn update_style(self, style: &WindowStyle) -> Result<()> {
pub fn update_style(self, style: &WindowStyle) -> eyre::Result<()> {
WindowsApi::update_style(self.hwnd, i32::try_from(style.bits())?)
}
#[cfg(target_pointer_width = "64")]
pub fn update_ex_style(self, style: &ExtendedWindowStyle) -> Result<()> {
pub fn update_ex_style(self, style: &ExtendedWindowStyle) -> eyre::Result<()> {
WindowsApi::update_ex_style(self.hwnd, isize::try_from(style.bits())?)
}
#[cfg(target_pointer_width = "32")]
pub fn update_ex_style(self, style: &ExtendedWindowStyle) -> Result<()> {
pub fn update_ex_style(self, style: &ExtendedWindowStyle) -> eyre::Result<()> {
WindowsApi::update_ex_style(self.hwnd, i32::try_from(style.bits())?)
}
pub fn style(self) -> Result<WindowStyle> {
pub fn style(self) -> eyre::Result<WindowStyle> {
let bits = u32::try_from(WindowsApi::gwl_style(self.hwnd)?)?;
Ok(WindowStyle::from_bits_truncate(bits))
}
pub fn ex_style(self) -> Result<ExtendedWindowStyle> {
pub fn ex_style(self) -> eyre::Result<ExtendedWindowStyle> {
let bits = u32::try_from(WindowsApi::gwl_ex_style(self.hwnd)?)?;
Ok(ExtendedWindowStyle::from_bits_truncate(bits))
}
pub fn title(self) -> Result<String> {
pub fn title(self) -> eyre::Result<String> {
WindowsApi::window_text_w(self.hwnd)
}
pub fn path(self) -> Result<String> {
pub fn path(self) -> eyre::Result<String> {
let (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd);
let handle = WindowsApi::process_handle(process_id)?;
let path = WindowsApi::exe_path(handle);
@@ -716,7 +715,7 @@ impl Window {
path
}
pub fn exe(self) -> Result<String> {
pub fn exe(self) -> eyre::Result<String> {
let (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd);
let handle = WindowsApi::process_handle(process_id)?;
let exe = WindowsApi::exe(handle);
@@ -729,11 +728,11 @@ impl Window {
process_id
}
pub fn class(self) -> Result<String> {
pub fn class(self) -> eyre::Result<String> {
WindowsApi::real_window_class_w(self.hwnd)
}
pub fn is_cloaked(self) -> Result<bool> {
pub fn is_cloaked(self) -> eyre::Result<bool> {
WindowsApi::is_window_cloaked(self.hwnd)
}
@@ -741,14 +740,14 @@ impl Window {
WindowsApi::is_window(self.hwnd)
}
pub fn remove_title_bar(self) -> Result<()> {
pub fn remove_title_bar(self) -> eyre::Result<()> {
let mut style = self.style()?;
style.remove(WindowStyle::CAPTION);
style.remove(WindowStyle::THICKFRAME);
self.update_style(&style)
}
pub fn add_title_bar(self) -> Result<()> {
pub fn add_title_bar(self) -> eyre::Result<()> {
let mut style = self.style()?;
style.insert(WindowStyle::CAPTION);
style.insert(WindowStyle::THICKFRAME);
@@ -759,7 +758,7 @@ impl Window {
/// it. Use raise_and_focus_window to activate and focus a window.
/// It also checks if there is a border attached to this window and if it is
/// it raises it as well.
pub fn raise(self) -> Result<()> {
pub fn raise(self) -> eyre::Result<()> {
WindowsApi::raise_window(self.hwnd)?;
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
WindowsApi::raise_window(border_info.border_hwnd)?;
@@ -771,7 +770,7 @@ impl Window {
/// it.
/// It also checks if there is a border attached to this window and if it is
/// it lowers it as well.
pub fn lower(self) -> Result<()> {
pub fn lower(self) -> eyre::Result<()> {
WindowsApi::lower_window(self.hwnd)?;
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
WindowsApi::lower_window(border_info.border_hwnd)?;
@@ -784,7 +783,7 @@ impl Window {
self,
event: Option<WindowManagerEvent>,
debug: &mut RuleDebug,
) -> Result<bool> {
) -> eyre::Result<bool> {
if !self.is_window() {
return Ok(false);
}
@@ -817,13 +816,13 @@ impl Window {
let mut allow_cloaked = false;
if let Some(event) = event {
if matches!(
if let Some(event) = event
&& matches!(
event,
WindowManagerEvent::Hide(_, _) | WindowManagerEvent::Cloak(_, _)
) {
allow_cloaked = true;
}
)
{
allow_cloaked = true;
}
debug.allow_cloaked = allow_cloaked;
@@ -1302,31 +1301,31 @@ pub fn should_act_individual(
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
if let Some(re) = regex_identifiers.get(&identifier.id)
&& re.is_match(title)
{
should_act = true;
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
if let Some(re) = regex_identifiers.get(&identifier.id)
&& re.is_match(class)
{
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
if let Some(re) = regex_identifiers.get(&identifier.id)
&& re.is_match(exe_name)
{
should_act = true;
}
}
ApplicationIdentifier::Path => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(path) {
should_act = true;
}
if let Some(re) = regex_identifiers.get(&identifier.id)
&& re.is_match(path)
{
should_act = true;
}
}
},

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,12 @@ use std::fmt::Formatter;
use serde::Deserialize;
use serde::Serialize;
use crate::window::should_act;
use crate::window::Window;
use crate::winevent::WinEvent;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST;
use crate::REGEX_IDENTIFIERS;
use crate::window::Window;
use crate::window::should_act;
use crate::winevent::WinEvent;
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

View File

@@ -1,18 +1,15 @@
use color_eyre::eyre::anyhow;
use color_eyre::eyre::bail;
use color_eyre::eyre;
use color_eyre::eyre::Error;
use color_eyre::Result;
use color_eyre::eyre::OptionExt;
use color_eyre::eyre::bail;
use core::ffi::c_void;
use std::collections::HashMap;
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::mem::size_of;
use std::path::Path;
use windows::core::Result as WindowsCrateResult;
use windows::core::PCWSTR;
use windows::core::PWSTR;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Foundation::COLORREF;
use windows::Win32::Foundation::CloseHandle;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::Foundation::HMODULE;
@@ -21,8 +18,9 @@ use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::POINT;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
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::Dwm::DWMWA_BORDER_COLOR;
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
use windows::Win32::Graphics::Dwm::DWMWA_COLOR_NONE;
@@ -30,57 +28,61 @@ use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
use windows::Win32::Graphics::Dwm::DWMWA_WINDOW_CORNER_PREFERENCE;
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
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::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use windows::Win32::Graphics::Gdi::HBRUSH;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
use windows::Win32::Graphics::Gdi::MONITORINFOEXW;
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::RoundRect;
use windows::Win32::Graphics::Gdi::HBRUSH;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
use windows::Win32::Graphics::Gdi::MONITORINFOEXW;
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use windows::Win32::System::Com::CoCreateInstance;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::System::Com::CoCreateInstance;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::Power::RegisterPowerSettingNotification;
use windows::Win32::System::Power::HPOWERNOTIFY;
use windows::Win32::System::Power::RegisterPowerSettingNotification;
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
use windows::Win32::System::RemoteDesktop::WTSRegisterSessionNotification;
use windows::Win32::System::Threading::GetCurrentProcessId;
use windows::Win32::System::Threading::OpenProcess;
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::System::Threading::QueryFullProcessImageNameW;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::HiDpi::GetDpiForMonitor;
use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyState;
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_LBUTTON;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
use windows::Win32::UI::Shell::DWPOS_FILL;
use windows::Win32::UI::Shell::DesktopWallpaper;
use windows::Win32::UI::Shell::IDesktopWallpaper;
use windows::Win32::UI::Shell::DWPOS_FILL;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
use windows::Win32::UI::WindowsAndMessaging::DEV_BROADCAST_DEVICEINTERFACE_W;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
@@ -91,15 +93,38 @@ use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
use windows::Win32::UI::WindowsAndMessaging::GetWindowTextW;
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use windows::Win32::UI::WindowsAndMessaging::HDEVNOTIFY;
use windows::Win32::UI::WindowsAndMessaging::HWND_BOTTOM;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOP;
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::IsZoomed;
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
use windows::Win32::UI::WindowsAndMessaging::RegisterDeviceNotificationW;
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use windows::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_MINIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_NORMAL;
use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
@@ -108,35 +133,6 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::ShowWindowAsync;
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
use windows::Win32::UI::WindowsAndMessaging::DEV_BROADCAST_DEVICEINTERFACE_W;
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use windows::Win32::UI::WindowsAndMessaging::HDEVNOTIFY;
use windows::Win32::UI::WindowsAndMessaging::HWND_BOTTOM;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOP;
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETFOREGROUNDLOCKTIMEOUT;
use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
use windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use windows::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_MINIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_NORMAL;
use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNOACTIVATE;
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::WNDCLASSW;
@@ -147,24 +143,28 @@ use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::core::PCWSTR;
use windows::core::PWSTR;
use windows::core::Result as WindowsCrateResult;
use windows_core::BOOL;
use windows_core::HSTRING;
use crate::core::Rect;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::WINDOW_HANDLING_BEHAVIOUR;
use crate::Window;
use crate::WindowHandlingBehaviour;
use crate::WindowManager;
use crate::container::Container;
use crate::monitor;
use crate::monitor::Monitor;
use crate::ring::Ring;
use crate::set_window_position::SetWindowPosition;
use crate::windows_callbacks;
use crate::Window;
use crate::WindowHandlingBehaviour;
use crate::WindowManager;
use crate::DISPLAY_INDEX_PREFERENCES;
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::WINDOW_HANDLING_BEHAVIOUR;
macro_rules! as_ptr {
($value:expr) => {
@@ -207,7 +207,7 @@ impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
}
pub trait ProcessWindowsCrateResult<T> {
fn process(self) -> Result<T>;
fn process(self) -> eyre::Result<T>;
}
macro_rules! impl_process_windows_crate_integer_wrapper_result {
@@ -215,7 +215,7 @@ macro_rules! impl_process_windows_crate_integer_wrapper_result {
paste::paste! {
$(
impl ProcessWindowsCrateResult<$deref> for $input {
fn process(self) -> Result<$deref> {
fn process(self) -> eyre::Result<$deref> {
if self == $input(std::ptr::null_mut()) {
Err(std::io::Error::last_os_error().into())
} else {
@@ -233,7 +233,7 @@ impl_process_windows_crate_integer_wrapper_result!(
);
impl<T> ProcessWindowsCrateResult<T> for WindowsCrateResult<T> {
fn process(self) -> Result<T> {
fn process(self) -> eyre::Result<T> {
match self {
Ok(value) => Ok(value),
Err(error) => Err(error.into()),
@@ -247,13 +247,13 @@ impl WindowsApi {
pub fn enum_display_monitors(
callback: MONITORENUMPROC,
callback_data_address: isize,
) -> Result<()> {
) -> eyre::Result<()> {
unsafe { EnumDisplayMonitors(None, None, callback, LPARAM(callback_data_address)) }
.ok()
.process()
}
pub fn valid_hmonitors() -> Result<Vec<(String, isize)>> {
pub fn valid_hmonitors() -> eyre::Result<Vec<(String, isize)>> {
Ok(win32_display_data::connected_displays_all()
.flatten()
.map(|d| {
@@ -265,7 +265,7 @@ impl WindowsApi {
.collect::<Vec<_>>())
}
pub fn load_monitor_information(wm: &mut WindowManager) -> Result<()> {
pub fn load_monitor_information(wm: &mut WindowManager) -> eyre::Result<()> {
let monitors = &mut wm.monitors;
let monitor_usr_idx_map = &mut wm.monitor_usr_idx_map;
@@ -282,12 +282,12 @@ impl WindowsApi {
}
for d in &all_displays {
if let Some(id) = &d.serial_number_id {
if serial_id_map.get(id).copied().unwrap_or_default() > 1 {
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
if !dupes.contains(id) {
(*dupes).push(id.clone());
}
if let Some(id) = &d.serial_number_id
&& serial_id_map.get(id).copied().unwrap_or_default() > 1
{
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
if !dupes.contains(id) {
(*dupes).push(id.clone());
}
}
}
@@ -310,7 +310,7 @@ impl WindowsApi {
let name = name.split('\\').collect::<Vec<_>>()[0].to_string();
for monitor in monitors.elements() {
if device_id.eq(monitor.device_id()) {
if device_id.eq(&monitor.device_id) {
continue 'read;
}
}
@@ -335,15 +335,14 @@ impl WindowsApi {
let mut index_preference = None;
let monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
for (index, monitor_size) in &*monitor_index_preferences {
if m.size() == monitor_size {
if m.size == *monitor_size {
index_preference = Option::from(index);
}
}
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
for (index, id) in &*display_index_preferences {
if m.serial_number_id().as_ref().is_some_and(|sn| sn == id) || id.eq(m.device_id())
{
if m.serial_number_id.as_ref().is_some_and(|sn| sn == id) || id.eq(&m.device_id) {
index_preference = Option::from(index);
}
}
@@ -356,7 +355,7 @@ impl WindowsApi {
let current_name = monitors
.elements_mut()
.get(*preference)
.map_or("", |m| m.name());
.map_or("", |m| &m.name);
if current_name == "PLACEHOLDER" {
let _ = monitors.elements_mut().remove(*preference);
monitors.elements_mut().insert(*preference, m);
@@ -368,16 +367,14 @@ impl WindowsApi {
}
}
monitors
.elements_mut()
.retain(|m| m.name().ne("PLACEHOLDER"));
monitors.elements_mut().retain(|m| m.name.ne("PLACEHOLDER"));
// Rebuild monitor index map
*monitor_usr_idx_map = HashMap::new();
let mut added_monitor_idxs = Vec::new();
for (index, id) in &*DISPLAY_INDEX_PREFERENCES.read() {
if let Some(m_idx) = monitors.elements().iter().position(|m| {
m.serial_number_id().as_ref().is_some_and(|sn| sn == id) || m.device_id() == id
m.serial_number_id.as_ref().is_some_and(|sn| sn == id) || m.device_id.eq(id)
}) {
monitor_usr_idx_map.insert(*index, m_idx);
added_monitor_idxs.push(m_idx);
@@ -409,13 +406,13 @@ impl WindowsApi {
Ok(())
}
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> Result<()> {
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> eyre::Result<()> {
unsafe { EnumWindows(callback, LPARAM(callback_data_address)) }.process()
}
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> eyre::Result<()> {
for monitor in monitors.elements_mut() {
let monitor_name = monitor.name().clone();
let monitor_name = monitor.name.clone();
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
// EnumWindows will enumerate through windows on all monitors
Self::enum_windows(
@@ -426,7 +423,7 @@ impl WindowsApi {
// Ensure that the resize_dimensions Vec length matches the number of containers for
// the potential later calls to workspace.remove_window later in this fn
let len = workspace.containers().len();
workspace.resize_dimensions_mut().resize(len, None);
workspace.resize_dimensions.resize(len, None);
// We have to prune each monitor's primary workspace of undesired windows here
let mut windows_on_other_monitors = vec![];
@@ -448,7 +445,7 @@ impl WindowsApi {
Ok(())
}
pub fn allow_set_foreground_window(process_id: u32) -> Result<()> {
pub fn allow_set_foreground_window(process_id: u32) -> eyre::Result<()> {
unsafe { AllowSetForegroundWindow(process_id) }.process()
}
@@ -458,13 +455,13 @@ impl WindowsApi {
unsafe { MonitorFromWindow(HWND(as_ptr!(hwnd)), MONITOR_DEFAULTTONEAREST) }.0 as isize
}
pub fn monitor_name_from_window(hwnd: isize) -> Result<String> {
pub fn monitor_name_from_window(hwnd: isize) -> eyre::Result<String> {
// MONITOR_DEFAULTTONEAREST ensures that the return value will never be NULL
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
Ok(Self::monitor(
unsafe { MonitorFromWindow(HWND(as_ptr!(hwnd)), MONITOR_DEFAULTTONEAREST) }.0 as isize,
)?
.name()
.name
.to_string())
}
@@ -482,7 +479,7 @@ impl WindowsApi {
layout: &Rect,
top: bool,
with_async_window_pos: bool,
) -> Result<()> {
) -> eyre::Result<()> {
let hwnd = HWND(as_ptr!(hwnd));
let mut flags = SetWindowPosition::NO_ACTIVATE
@@ -533,13 +530,13 @@ impl WindowsApi {
Self::set_window_pos(hwnd, &rect, HWND_TOP, flags.bits())
}
pub fn bring_window_to_top(hwnd: isize) -> Result<()> {
pub fn bring_window_to_top(hwnd: isize) -> eyre::Result<()> {
unsafe { BringWindowToTop(HWND(as_ptr!(hwnd))) }.process()
}
/// Raise the window to the top of the Z order, but do not activate or focus
/// it. Use raise_and_focus_window to activate and focus a window.
pub fn raise_window(hwnd: isize) -> Result<()> {
pub fn raise_window(hwnd: isize) -> eyre::Result<()> {
let mut flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE
@@ -563,7 +560,7 @@ impl WindowsApi {
/// Lower the window to the bottom of the Z order, but do not activate or focus
/// it.
pub fn lower_window(hwnd: isize) -> Result<()> {
pub fn lower_window(hwnd: isize) -> eyre::Result<()> {
let mut flags = SetWindowPosition::NO_MOVE
| SetWindowPosition::NO_SIZE
| SetWindowPosition::NO_ACTIVATE
@@ -585,7 +582,7 @@ impl WindowsApi {
)
}
pub fn set_border_pos(hwnd: isize, layout: &Rect, position: isize) -> Result<()> {
pub fn set_border_pos(hwnd: isize, layout: &Rect, position: isize) -> eyre::Result<()> {
let mut flags = SetWindowPosition::NO_SEND_CHANGING
| SetWindowPosition::NO_ACTIVATE
| SetWindowPosition::NO_REDRAW
@@ -607,7 +604,7 @@ impl WindowsApi {
}
/// set_window_pos calls SetWindowPos without any accounting for Window decorations.
fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> eyre::Result<()> {
unsafe {
SetWindowPos(
hwnd,
@@ -623,7 +620,7 @@ impl WindowsApi {
}
/// move_windows calls MoveWindow, but cannot be called with async window pos, so it might hang
pub fn move_window(hwnd: isize, layout: &Rect, repaint: bool) -> Result<()> {
pub fn move_window(hwnd: isize, layout: &Rect, repaint: bool) -> eyre::Result<()> {
let hwnd = HWND(as_ptr!(hwnd));
let shadow_rect = Self::shadow_rect(hwnd).unwrap_or_default();
@@ -658,15 +655,16 @@ impl WindowsApi {
Self::show_window(hwnd, SW_MINIMIZE);
}
fn post_message(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> Result<()> {
fn post_message(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> eyre::Result<()> {
unsafe { PostMessageW(Option::from(hwnd), message, wparam, lparam) }.process()
}
pub fn close_window(hwnd: isize) -> Result<()> {
match Self::post_message(HWND(as_ptr!(hwnd)), WM_CLOSE, WPARAM(0), LPARAM(0)) {
Ok(()) => Ok(()),
Err(_) => Err(anyhow!("could not close window")),
pub fn close_window(hwnd: isize) -> eyre::Result<()> {
if Self::post_message(HWND(as_ptr!(hwnd)), WM_CLOSE, WPARAM(0), LPARAM(0)).is_err() {
bail!("could not close window");
}
Ok(())
}
pub fn hide_window(hwnd: isize) {
@@ -685,11 +683,11 @@ impl WindowsApi {
Self::show_window(hwnd, SW_MAXIMIZE);
}
pub fn foreground_window() -> Result<isize> {
pub fn foreground_window() -> eyre::Result<isize> {
unsafe { GetForegroundWindow() }.process()
}
pub fn raise_and_focus_window(hwnd: isize) -> Result<()> {
pub fn raise_and_focus_window(hwnd: isize) -> eyre::Result<()> {
let event = [INPUT {
r#type: INPUT_MOUSE,
..Default::default()
@@ -717,20 +715,20 @@ impl WindowsApi {
}
#[allow(dead_code)]
pub fn top_window() -> Result<isize> {
pub fn top_window() -> eyre::Result<isize> {
unsafe { GetTopWindow(None)? }.process()
}
pub fn desktop_window() -> Result<isize> {
pub fn desktop_window() -> eyre::Result<isize> {
unsafe { GetDesktopWindow() }.process()
}
#[allow(dead_code)]
pub fn next_window(hwnd: isize) -> Result<isize> {
pub fn next_window(hwnd: isize) -> eyre::Result<isize> {
unsafe { GetWindow(HWND(as_ptr!(hwnd)), GW_HWNDNEXT)? }.process()
}
pub fn alt_tab_windows() -> Result<Vec<Window>> {
pub fn alt_tab_windows() -> eyre::Result<Vec<Window>> {
let mut hwnds = vec![];
Self::enum_windows(
Some(windows_callbacks::alt_tab_windows),
@@ -741,7 +739,7 @@ impl WindowsApi {
}
#[allow(dead_code)]
pub fn top_visible_window() -> Result<isize> {
pub fn top_visible_window() -> eyre::Result<isize> {
let hwnd = Self::top_window()?;
let mut next_hwnd = hwnd;
@@ -753,10 +751,10 @@ impl WindowsApi {
next_hwnd = Self::next_window(next_hwnd)?;
}
Err(anyhow!("could not find next window"))
bail!("could not find next window")
}
pub fn window_rect(hwnd: isize) -> Result<Rect> {
pub fn window_rect(hwnd: isize) -> eyre::Result<Rect> {
let mut rect = unsafe { std::mem::zeroed() };
if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_ok() {
@@ -775,7 +773,7 @@ impl WindowsApi {
/// the window painted region. The four values in the returned Rect can be
/// added to a position rect to compute a size for set_window_pos that will
/// fill the target area, ignoring shadows.
fn shadow_rect(hwnd: HWND) -> Result<Rect> {
fn shadow_rect(hwnd: HWND) -> eyre::Result<Rect> {
let window_rect = Self::window_rect(hwnd.0 as isize)?;
let mut srect = Default::default();
@@ -810,26 +808,26 @@ impl WindowsApi {
let _ = Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
}
}
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
fn set_cursor_pos(x: i32, y: i32) -> eyre::Result<()> {
unsafe { SetCursorPos(x, y) }.process()
}
pub fn cursor_pos() -> Result<POINT> {
pub fn cursor_pos() -> eyre::Result<POINT> {
let mut cursor_pos = POINT::default();
unsafe { GetCursorPos(&mut cursor_pos) }.process()?;
Ok(cursor_pos)
}
pub fn window_from_point(point: POINT) -> Result<isize> {
pub fn window_from_point(point: POINT) -> eyre::Result<isize> {
unsafe { WindowFromPoint(point) }.process()
}
pub fn window_at_cursor_pos() -> Result<isize> {
pub fn window_at_cursor_pos() -> eyre::Result<isize> {
Self::window_from_point(Self::cursor_pos()?)
}
pub fn center_cursor_in_rect(rect: &Rect) -> Result<()> {
pub fn center_cursor_in_rect(rect: &Rect) -> eyre::Result<()> {
Self::set_cursor_pos(rect.left + (rect.right / 2), rect.top + (rect.bottom / 2))
}
@@ -852,17 +850,17 @@ impl WindowsApi {
unsafe { GetCurrentProcessId() }
}
pub fn process_id_to_session_id() -> Result<u32> {
pub fn process_id_to_session_id() -> eyre::Result<u32> {
let process_id = Self::current_process_id();
let mut session_id = 0;
unsafe {
if ProcessIdToSessionId(process_id, &mut session_id).is_ok() {
Ok(session_id)
} else {
Err(anyhow!("could not determine current session id"))
if ProcessIdToSessionId(process_id, &mut session_id).is_err() {
bail!("could not determine current session id")
}
}
Ok(session_id)
}
#[cfg(target_pointer_width = "64")]
@@ -870,7 +868,7 @@ impl WindowsApi {
hwnd: HWND,
index: WINDOW_LONG_PTR_INDEX,
new_value: isize,
) -> Result<()> {
) -> eyre::Result<()> {
Result::from(WindowsResult::from(unsafe {
SetWindowLongPtrW(hwnd, index, new_value)
}))
@@ -882,7 +880,7 @@ impl WindowsApi {
hwnd: HWND,
index: WINDOW_LONG_PTR_INDEX,
new_value: i32,
) -> Result<()> {
) -> eyre::Result<()> {
Result::from(WindowsResult::from(unsafe {
SetWindowLongPtrW(hwnd, index, new_value)
}))
@@ -890,27 +888,27 @@ impl WindowsApi {
}
#[cfg(target_pointer_width = "64")]
pub fn gwl_style(hwnd: isize) -> Result<isize> {
pub fn gwl_style(hwnd: isize) -> eyre::Result<isize> {
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE)
}
#[cfg(target_pointer_width = "32")]
pub fn gwl_style(hwnd: isize) -> Result<i32> {
pub fn gwl_style(hwnd: isize) -> eyre::Result<i32> {
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE)
}
#[cfg(target_pointer_width = "64")]
pub fn gwl_ex_style(hwnd: isize) -> Result<isize> {
pub fn gwl_ex_style(hwnd: isize) -> eyre::Result<isize> {
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE)
}
#[cfg(target_pointer_width = "32")]
pub fn gwl_ex_style(hwnd: isize) -> Result<i32> {
pub fn gwl_ex_style(hwnd: isize) -> eyre::Result<i32> {
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE)
}
#[cfg(target_pointer_width = "64")]
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> Result<isize> {
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> eyre::Result<isize> {
// Can return 0, which does not always mean that an error has occurred
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
Result::from(WindowsResult::from(unsafe {
@@ -919,7 +917,7 @@ impl WindowsApi {
}
#[cfg(target_pointer_width = "32")]
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> Result<i32> {
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> eyre::Result<i32> {
// Can return 0, which does not always mean that an error has occurred
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
Result::from(WindowsResult::from(unsafe {
@@ -928,26 +926,26 @@ impl WindowsApi {
}
#[cfg(target_pointer_width = "64")]
pub fn update_style(hwnd: isize, new_value: isize) -> Result<()> {
pub fn update_style(hwnd: isize, new_value: isize) -> eyre::Result<()> {
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE, new_value)
}
#[cfg(target_pointer_width = "32")]
pub fn update_style(hwnd: isize, new_value: i32) -> Result<()> {
pub fn update_style(hwnd: isize, new_value: i32) -> eyre::Result<()> {
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE, new_value)
}
#[cfg(target_pointer_width = "64")]
pub fn update_ex_style(hwnd: isize, new_value: isize) -> Result<()> {
pub fn update_ex_style(hwnd: isize, new_value: isize) -> eyre::Result<()> {
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE, new_value)
}
#[cfg(target_pointer_width = "32")]
pub fn update_ex_style(hwnd: isize, new_value: i32) -> Result<()> {
pub fn update_ex_style(hwnd: isize, new_value: i32) -> eyre::Result<()> {
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE, new_value)
}
pub fn window_text_w(hwnd: isize) -> Result<String> {
pub fn window_text_w(hwnd: isize) -> eyre::Result<String> {
let mut text: [u16; 512] = [0; 512];
match WindowsResult::from(unsafe { GetWindowTextW(HWND(as_ptr!(hwnd)), &mut text) }) {
WindowsResult::Ok(len) => {
@@ -962,19 +960,19 @@ impl WindowsApi {
access_rights: PROCESS_ACCESS_RIGHTS,
inherit_handle: bool,
process_id: u32,
) -> Result<HANDLE> {
) -> eyre::Result<HANDLE> {
unsafe { OpenProcess(access_rights, inherit_handle, process_id) }.process()
}
pub fn close_process(handle: HANDLE) -> Result<()> {
pub fn close_process(handle: HANDLE) -> eyre::Result<()> {
unsafe { CloseHandle(handle) }.process()
}
pub fn process_handle(process_id: u32) -> Result<HANDLE> {
pub fn process_handle(process_id: u32) -> eyre::Result<HANDLE> {
Self::open_process(PROCESS_QUERY_INFORMATION, false, process_id)
}
pub fn exe_path(handle: HANDLE) -> Result<String> {
pub fn exe_path(handle: HANDLE) -> eyre::Result<String> {
let mut len = 260_u32;
let mut path: Vec<u16> = vec![0; len as usize];
let text_ptr = path.as_mut_ptr();
@@ -987,15 +985,15 @@ impl WindowsApi {
Ok(String::from_utf16(&path[..len as usize])?)
}
pub fn exe(handle: HANDLE) -> Result<String> {
pub fn exe(handle: HANDLE) -> eyre::Result<String> {
Ok(Self::exe_path(handle)?
.split('\\')
.next_back()
.ok_or_else(|| anyhow!("there is no last element"))?
.ok_or_eyre("there is no last element")?
.to_string())
}
pub fn real_window_class_w(hwnd: isize) -> Result<String> {
pub fn real_window_class_w(hwnd: isize) -> eyre::Result<String> {
const BUF_SIZE: usize = 512;
let mut class: [u16; BUF_SIZE] = [0; BUF_SIZE];
@@ -1010,7 +1008,7 @@ impl WindowsApi {
hwnd: isize,
attribute: DWMWINDOWATTRIBUTE,
value: &mut T,
) -> Result<()> {
) -> eyre::Result<()> {
unsafe {
DwmGetWindowAttribute(
HWND(as_ptr!(hwnd)),
@@ -1023,7 +1021,7 @@ impl WindowsApi {
Ok(())
}
pub fn is_window_cloaked(hwnd: isize) -> Result<bool> {
pub fn is_window_cloaked(hwnd: isize) -> eyre::Result<bool> {
let mut cloaked: u32 = 0;
Self::dwm_get_window_attribute(hwnd, DWMWA_CLOAKED, &mut cloaked)?;
@@ -1049,7 +1047,7 @@ impl WindowsApi {
unsafe { IsZoomed(HWND(as_ptr!(hwnd))) }.into()
}
pub fn monitor_info_w(hmonitor: HMONITOR) -> Result<MONITORINFOEXW> {
pub fn monitor_info_w(hmonitor: HMONITOR) -> eyre::Result<MONITORINFOEXW> {
let mut ex_info = MONITORINFOEXW::default();
ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;
unsafe { GetMonitorInfoW(hmonitor, &mut ex_info.monitorInfo) }
@@ -1069,7 +1067,7 @@ impl WindowsApi {
None
}
pub fn monitor(hmonitor: isize) -> Result<Monitor> {
pub fn monitor(hmonitor: isize) -> eyre::Result<Monitor> {
for mut display in win32_display_data::connected_displays_all().flatten() {
if display.hmonitor == hmonitor {
let path = display.device_path;
@@ -1112,7 +1110,7 @@ impl WindowsApi {
bail!("could not find device_id for hmonitor: {hmonitor}");
}
pub fn set_process_dpi_awareness_context() -> Result<()> {
pub fn set_process_dpi_awareness_context() -> eyre::Result<()> {
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }
.process()
}
@@ -1123,13 +1121,13 @@ impl WindowsApi {
ui_param: u32,
pv_param: *mut c_void,
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
) -> Result<()> {
) -> eyre::Result<()> {
unsafe { SystemParametersInfoW(action, ui_param, Option::from(pv_param), update_flags) }
.process()
}
#[tracing::instrument]
pub fn foreground_lock_timeout() -> Result<()> {
pub fn foreground_lock_timeout() -> eyre::Result<()> {
let mut value: u32 = 0;
Self::system_parameters_info_w(
@@ -1142,7 +1140,9 @@ impl WindowsApi {
tracing::info!("current value of ForegroundLockTimeout is {value}");
if value != 0 {
tracing::info!("updating value of ForegroundLockTimeout to {value} in order to enable keyboard-driven focus updating");
tracing::info!(
"updating value of ForegroundLockTimeout to {value} in order to enable keyboard-driven focus updating"
);
Self::system_parameters_info_w(
SPI_SETFOREGROUNDLOCKTIMEOUT,
@@ -1165,7 +1165,7 @@ impl WindowsApi {
}
#[allow(dead_code)]
pub fn focus_follows_mouse() -> Result<bool> {
pub fn focus_follows_mouse() -> eyre::Result<bool> {
let mut is_enabled: BOOL = unsafe { std::mem::zeroed() };
Self::system_parameters_info_w(
@@ -1179,7 +1179,7 @@ impl WindowsApi {
}
#[allow(dead_code)]
pub fn enable_focus_follows_mouse() -> Result<()> {
pub fn enable_focus_follows_mouse() -> eyre::Result<()> {
#[allow(clippy::manual_dangling_ptr)]
Self::system_parameters_info_w(
SPI_SETACTIVEWINDOWTRACKING,
@@ -1190,7 +1190,7 @@ impl WindowsApi {
}
#[allow(dead_code)]
pub fn disable_focus_follows_mouse() -> Result<()> {
pub fn disable_focus_follows_mouse() -> eyre::Result<()> {
Self::system_parameters_info_w(
SPI_SETACTIVEWINDOWTRACKING,
0,
@@ -1199,7 +1199,7 @@ impl WindowsApi {
)
}
pub fn module_handle_w() -> Result<HMODULE> {
pub fn module_handle_w() -> eyre::Result<HMODULE> {
unsafe { GetModuleHandleW(None) }.process()
}
@@ -1207,11 +1207,11 @@ impl WindowsApi {
unsafe { CreateSolidBrush(COLORREF(colour)) }
}
pub fn register_class_w(window_class: &WNDCLASSW) -> Result<u16> {
pub fn register_class_w(window_class: &WNDCLASSW) -> eyre::Result<u16> {
Result::from(WindowsResult::from(unsafe { RegisterClassW(window_class) }))
}
pub fn dpi_for_monitor(hmonitor: isize) -> Result<f32> {
pub fn dpi_for_monitor(hmonitor: isize) -> eyre::Result<f32> {
let mut dpi_x = u32::default();
let mut dpi_y = u32::default();
@@ -1229,14 +1229,14 @@ impl WindowsApi {
Ok(dpi_y as f32 / 96.0)
}
pub fn monitors_have_same_dpi(hmonitor_a: isize, hmonitor_b: isize) -> Result<bool> {
pub fn monitors_have_same_dpi(hmonitor_a: isize, hmonitor_b: isize) -> eyre::Result<bool> {
let dpi_a = Self::dpi_for_monitor(hmonitor_a)?;
let dpi_b = Self::dpi_for_monitor(hmonitor_b)?;
Ok((dpi_a - dpi_b).abs() < f32::EPSILON)
}
pub fn round_corners(hwnd: isize) -> Result<()> {
pub fn round_corners(hwnd: isize) -> eyre::Result<()> {
let round = DWMWCP_ROUND;
unsafe {
@@ -1250,7 +1250,7 @@ impl WindowsApi {
.process()
}
pub fn set_window_accent(hwnd: isize, color: Option<u32>) -> Result<()> {
pub fn set_window_accent(hwnd: isize, color: Option<u32>) -> eyre::Result<()> {
let col_ref = COLORREF(color.unwrap_or(DWMWA_COLOR_NONE));
unsafe {
DwmSetWindowAttribute(
@@ -1267,7 +1267,7 @@ impl WindowsApi {
name: PCWSTR,
instance: isize,
border: *mut Border,
) -> Result<isize> {
) -> eyre::Result<isize> {
unsafe {
CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
@@ -1287,7 +1287,7 @@ impl WindowsApi {
.process()
}
pub fn set_transparent(hwnd: isize, alpha: u8) -> Result<()> {
pub fn set_transparent(hwnd: isize, alpha: u8) -> eyre::Result<()> {
unsafe {
#[allow(clippy::cast_sign_loss)]
SetLayeredWindowAttributes(
@@ -1301,7 +1301,7 @@ impl WindowsApi {
Ok(())
}
pub fn get_transparent(hwnd: isize) -> Result<u8> {
pub fn get_transparent(hwnd: isize) -> eyre::Result<u8> {
unsafe {
let mut alpha: u8 = u8::default();
let mut color_ref = COLORREF(-1i32 as u32);
@@ -1316,7 +1316,7 @@ impl WindowsApi {
}
}
pub fn create_hidden_window(name: PCWSTR, instance: isize) -> Result<isize> {
pub fn create_hidden_window(name: PCWSTR, instance: isize) -> eyre::Result<isize> {
unsafe {
CreateWindowExW(
WS_EX_NOACTIVATE,
@@ -1410,11 +1410,11 @@ impl WindowsApi {
}
}
pub fn wts_register_session_notification(hwnd: isize) -> Result<()> {
pub fn wts_register_session_notification(hwnd: isize) -> eyre::Result<()> {
unsafe { WTSRegisterSessionNotification(HWND(as_ptr!(hwnd)), 1) }.process()
}
pub fn set_wallpaper(path: &Path, hmonitor: isize) -> Result<()> {
pub fn set_wallpaper(path: &Path, hmonitor: isize) -> eyre::Result<()> {
let path = path.canonicalize()?;
let wallpaper: IDesktopWallpaper =
@@ -1438,7 +1438,7 @@ impl WindowsApi {
Ok(())
}
pub fn get_wallpaper(hmonitor: isize) -> Result<String> {
pub fn get_wallpaper(hmonitor: isize) -> eyre::Result<String> {
let wallpaper: IDesktopWallpaper =
unsafe { CoCreateInstance(&DesktopWallpaper, None, CLSCTX_ALL)? };

View File

@@ -12,11 +12,11 @@ use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongW;
use windows::Win32::UI::WindowsAndMessaging::SendNotifyMessageW;
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongW;
use windows::Win32::UI::WindowsAndMessaging::OBJID_WINDOW;
use windows::Win32::UI::WindowsAndMessaging::SendNotifyMessageW;
use windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
@@ -33,16 +33,16 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
if is_visible && is_window && !is_minimized {
let window = Window::from(hwnd);
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default()) {
if should_manage {
if is_maximized {
WindowsApi::restore_window(window.hwnd);
}
let mut container = Container::default();
container.windows_mut().push_back(window);
containers.push_back(container);
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default())
&& should_manage
{
if is_maximized {
WindowsApi::restore_window(window.hwnd);
}
let mut container = Container::default();
container.windows_mut().push_back(window);
containers.push_back(container);
}
}
@@ -59,10 +59,10 @@ pub extern "system" fn alt_tab_windows(hwnd: HWND, lparam: LPARAM) -> BOOL {
if is_visible && is_window && !is_minimized {
let window = Window::from(hwnd);
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default()) {
if should_manage {
windows.push(window);
}
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default())
&& should_manage
{
windows.push(window);
}
}

View File

@@ -5,11 +5,11 @@ use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use windows::Win32::UI::Accessibility::SetWinEventHook;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MAX;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MIN;
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::WINEVENT_OUTOFCONTEXT;
use windows::Win32::UI::WindowsAndMessaging::WINEVENT_SKIPOWNPROCESS;

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,9 @@
[package]
name = "komorebic-no-console"
version = "0.1.38"
version = "0.1.39"
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
repository = "https://github.com/LGUG2Z/komorebi"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -1,9 +1,9 @@
[package]
name = "komorebic"
version = "0.1.38"
version = "0.1.39"
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
repository = "https://github.com/LGUG2Z/komorebi"
edition = "2021"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -2,8 +2,8 @@
#![allow(clippy::missing_errors_doc, clippy::doc_markdown)]
use chrono::Utc;
use komorebi_client::replace_env_in_path;
use komorebi_client::PathExt;
use komorebi_client::replace_env_in_path;
use std::fs::File;
use std::fs::OpenOptions;
use std::io;
@@ -20,15 +20,15 @@ use std::time::Duration;
use clap::CommandFactory;
use clap::Parser;
use clap::ValueEnum;
use color_eyre::eyre::anyhow;
use color_eyre::eyre;
use color_eyre::eyre::OptionExt;
use color_eyre::eyre::bail;
use color_eyre::Result;
use dirs::data_local_dir;
use fs_tail::TailedFile;
use komorebi_client::send_message;
use komorebi_client::send_query;
use komorebi_client::AppSpecificConfigurationPath;
use komorebi_client::ApplicationSpecificConfiguration;
use komorebi_client::send_message;
use komorebi_client::send_query;
use lazy_static::lazy_static;
use miette::NamedSource;
use miette::Report;
@@ -39,9 +39,9 @@ use serde::Deserialize;
use sysinfo::ProcessesToUpdate;
use which::which;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use komorebi_client::ApplicationConfigurationGenerator;
use komorebi_client::ApplicationIdentifier;
@@ -1527,7 +1527,7 @@ fn print_query(message: &SocketMessage) {
}
}
fn startup_dir() -> Result<PathBuf> {
fn startup_dir() -> eyre::Result<PathBuf> {
let startup = dirs::home_dir()
.expect("unable to obtain user's home folder")
.join("AppData")
@@ -1546,7 +1546,7 @@ fn startup_dir() -> Result<PathBuf> {
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
fn main() -> Result<()> {
fn main() -> eyre::Result<()> {
let opts: Opts = Opts::parse();
match opts.subcmd {
@@ -1585,7 +1585,7 @@ fn main() -> Result<()> {
path: &PathBuf,
content: &str,
created_files: &mut Vec<String>,
) -> Result<()> {
) -> eyre::Result<()> {
if path.exists() {
print!(
"{} will be overwritten, do you want to continue? (y/N): ",
@@ -1691,8 +1691,12 @@ fn main() -> Result<()> {
.env("TARGET_ARGS", arguments)
.output()?;
println!("NOTE: If your komorebi.json file contains a reference to $Env:KOMOREBI_CONFIG_HOME,");
println!("you need to add this to System Properties > Environment Variables > User Variables");
println!(
"NOTE: If your komorebi.json file contains a reference to $Env:KOMOREBI_CONFIG_HOME,"
);
println!(
"you need to add this to System Properties > Environment Variables > User Variables"
);
println!("in order for the autostart command to work properly");
}
SubCommand::DisableAutostart => {
@@ -1756,16 +1760,23 @@ fn main() -> Result<()> {
println!("{:?}", Report::new(diagnostic));
}
println!("Found komorebi.json; this file can be passed to the start command with the --config flag\n");
println!(
"Found komorebi.json; this file can be passed to the start command with the --config flag\n"
);
if let Ok(config) = StaticConfig::read(&static_config) {
match config.app_specific_configuration_path {
None => {
println!("Application specific configuration file path has not been set. Try running 'komorebic fetch-asc'\n");
println!(
"Application specific configuration file path has not been set. Try running 'komorebic fetch-asc'\n"
);
}
Some(AppSpecificConfigurationPath::Single(path)) => {
if !path.exists() {
println!("Application specific configuration file path '{}' does not exist. Try running 'komorebic fetch-asc'\n", path.display());
println!(
"Application specific configuration file path '{}' does not exist. Try running 'komorebic fetch-asc'\n",
path.display()
);
}
}
_ => {}
@@ -1783,9 +1794,14 @@ fn main() -> Result<()> {
StaticConfig::end_of_life(&raw);
if config_whkd.exists() {
println!("Found {}; key bindings will be loaded from here when whkd is started, and you can start it automatically using the --whkd flag\n", config_whkd.to_string_lossy());
println!(
"Found {}; key bindings will be loaded from here when whkd is started, and you can start it automatically using the --whkd flag\n",
config_whkd.to_string_lossy()
);
} else {
println!("No ~/.config/whkdrc found; you may not be able to control komorebi with your keyboard\n");
println!(
"No ~/.config/whkdrc found; you may not be able to control komorebi with your keyboard\n"
);
}
} else if config_pwsh.exists() {
println!("Found komorebi.ps1; this file will be autoloaded by komorebi\n");
@@ -1795,13 +1811,17 @@ fn main() -> Result<()> {
config_whkd.to_string_lossy()
);
} else {
println!("No ~/.config/whkdrc found; you may not be able to control komorebi with your keyboard\n");
println!(
"No ~/.config/whkdrc found; you may not be able to control komorebi with your keyboard\n"
);
}
} else if config_ahk.exists() {
println!("Found komorebi.ahk; this file will be autoloaded by komorebi\n");
} else {
println!("No komorebi configuration found in {home_display}\n");
println!("If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n");
println!(
"If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n"
);
}
let client = reqwest::blocking::Client::new();
@@ -1823,7 +1843,9 @@ fn main() -> Result<()> {
{
let trimmed = release.tag_name.trim_start_matches("v");
if trimmed > version {
println!("An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}");
println!(
"An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}"
);
}
}
}
@@ -2160,46 +2182,53 @@ fn main() -> Result<()> {
SubCommand::Start(arg) => {
let mut ahk: String = String::from("autohotkey.exe");
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE") {
if which(&komorebi_ahk_exe).is_ok() {
ahk = komorebi_ahk_exe;
}
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE")
&& which(&komorebi_ahk_exe).is_ok()
{
ahk = komorebi_ahk_exe;
}
if arg.whkd && which("whkd").is_err() {
bail!("could not find whkd, please make sure it is installed before using the --whkd flag");
bail!(
"could not find whkd, please make sure it is installed before using the --whkd flag"
);
}
if arg.masir && which("masir").is_err() {
bail!("could not find masir, please make sure it is installed before using the --masir flag");
bail!(
"could not find masir, please make sure it is installed before using the --masir flag"
);
}
if arg.ahk && which(&ahk).is_err() {
bail!("could not find autohotkey, please make sure it is installed before using the --ahk flag");
bail!(
"could not find autohotkey, please make sure it is installed before using the --ahk flag"
);
}
let mut buf: PathBuf;
// The komorebi.ps1 shim will only exist in the Path if installed by Scoop
let exec = if let Ok(output) = Command::new("where.exe").arg("komorebi.ps1").output() {
let stdout = String::from_utf8(output.stdout)?;
match stdout.trim() {
"" => None,
// It's possible that a komorebi.ps1 config will be in %USERPROFILE% - ignore this
stdout if !stdout.contains("scoop") => None,
stdout => {
buf = PathBuf::from(stdout);
buf.pop(); // %USERPROFILE%\scoop\shims
buf.pop(); // %USERPROFILE%\scoop
buf.push("apps\\komorebi\\current\\komorebi.exe"); //%USERPROFILE%\scoop\komorebi\current\komorebi.exe
Some(buf.to_str().ok_or_else(|| {
anyhow!("cannot create a string from the scoop komorebi path")
})?)
let exec =
if let Ok(output) = Command::new("where.exe").arg("komorebi.ps1").output() {
let stdout = String::from_utf8(output.stdout)?;
match stdout.trim() {
"" => None,
// It's possible that a komorebi.ps1 config will be in %USERPROFILE% - ignore this
stdout if !stdout.contains("scoop") => None,
stdout => {
buf = PathBuf::from(stdout);
buf.pop(); // %USERPROFILE%\scoop\shims
buf.pop(); // %USERPROFILE%\scoop
buf.push("apps\\komorebi\\current\\komorebi.exe"); //%USERPROFILE%\scoop\komorebi\current\komorebi.exe
Some(buf.to_str().ok_or_eyre(
"cannot create a string from the scoop komorebi path",
)?)
}
}
}
} else {
None
};
} else {
None
};
let mut flags = vec![];
if let Some(config) = &arg.config {
@@ -2331,33 +2360,18 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
komorebi_json.is_file().then_some(komorebi_json)
});
if arg.bar {
if let Some(config) = &static_config {
let mut config = StaticConfig::read(config)?;
if let Some(display_bar_configurations) = &mut config.bar_configurations {
for config_file_path in &mut *display_bar_configurations {
let script = format!(
r#"Start-Process "komorebi-bar" '"--config" "{}"' -WindowStyle hidden"#,
config_file_path.to_string_lossy()
);
if arg.bar
&& let Some(config) = &static_config
{
let mut config = StaticConfig::read(config)?;
if let Some(display_bar_configurations) = &mut config.bar_configurations {
for config_file_path in &mut *display_bar_configurations {
let script = format!(
r#"Start-Process "komorebi-bar" '"--config" "{}"' -WindowStyle hidden"#,
config_file_path.to_string_lossy()
);
match powershell_script::run(&script) {
Ok(_) => {
println!("{script}");
}
Err(error) => {
println!("Error: {error}");
}
}
}
} else {
let script = r"
if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
{
Start-Process komorebi-bar -WindowStyle hidden
}
";
match powershell_script::run(script) {
match powershell_script::run(&script) {
Ok(_) => {
println!("{script}");
}
@@ -2366,6 +2380,21 @@ if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
}
}
}
} else {
let script = r"
if (!(Get-Process komorebi-bar -ErrorAction SilentlyContinue))
{
Start-Process komorebi-bar -WindowStyle hidden
}
";
match powershell_script::run(script) {
Ok(_) => {
println!("{script}");
}
Err(error) => {
println!("Error: {error}");
}
}
}
}
@@ -2388,18 +2417,28 @@ if (!(Get-Process masir -ErrorAction SilentlyContinue))
println!("\nThank you for using komorebi!\n");
println!("# Commercial Use License");
println!("* View licensing options https://lgug2z.com/software/komorebi - A commercial use license is required to use komorebi at work");
println!(
"* View licensing options https://lgug2z.com/software/komorebi - A commercial use license is required to use komorebi at work"
);
println!("\n# Personal Use Sponsorship");
println!("* Become a sponsor https://github.com/sponsors/LGUG2Z - $5/month makes a big difference");
println!(
"* Become a sponsor https://github.com/sponsors/LGUG2Z - $5/month makes a big difference"
);
println!("* Leave a tip https://ko-fi.com/lgug2z - An alternative to GitHub Sponsors");
println!("\n# Community");
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
println!(
"* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops"
);
println!(
"* Subscribe to https://youtube.com/@LGUG2Z - Development videos, feature previews and release overviews"
);
println!("* Explore the Awesome Komorebi list https://github.com/LGUG2Z/awesome-komorebi - Projects in the komorebi ecosystem");
println!(
"* Explore the Awesome Komorebi list https://github.com/LGUG2Z/awesome-komorebi - Projects in the komorebi ecosystem"
);
println!("\n# Documentation");
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
println!(
"* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands"
);
let bar_config = arg.config.or_else(|| {
let bar_json = HOME_DIR.join("komorebi.bar.json");
@@ -2438,7 +2477,9 @@ if (!(Get-Process masir -ErrorAction SilentlyContinue))
{
let trimmed = release.tag_name.trim_start_matches("v");
if trimmed > version {
println!("An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}");
println!(
"An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}"
);
}
}
}
@@ -3085,7 +3126,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
file.write_all(formatted_content.as_bytes())?;
println!("File successfully formatted for PRs to https://github.com/LGUG2Z/komorebi-application-specific-configuration");
println!(
"File successfully formatted for PRs to https://github.com/LGUG2Z/komorebi-application-specific-configuration"
);
}
SubCommand::FetchAppSpecificConfiguration => {
let content = reqwest::blocking::get("https://raw.githubusercontent.com/LGUG2Z/komorebi-application-specific-configuration/master/applications.json")?
@@ -3101,10 +3144,12 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
file.write_all(content.as_bytes())?;
println!("Latest version of applications.json from https://github.com/LGUG2Z/komorebi-application-specific-configuration downloaded\n");
println!(
"You can add this to your komorebi.json static configuration file like this: \n\n\"app_specific_configuration_path\": \"{}\"",
output_file.display().to_string().replace("\\", "/")
"Latest version of applications.json from https://github.com/LGUG2Z/komorebi-application-specific-configuration downloaded\n"
);
println!(
"You can add this to your komorebi.json static configuration file like this: \n\n\"app_specific_configuration_path\": \"{}\"",
output_file.display().to_string().replace("\\", "/")
);
}
SubCommand::ApplicationSpecificConfigurationSchema => {
@@ -3134,14 +3179,14 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
SubCommand::StaticConfigSchema => {
#[cfg(feature = "schemars")]
{
let settings = schemars::gen::SchemaSettings::default().with(|s| {
let settings = schemars::r#gen::SchemaSettings::default().with(|s| {
s.option_nullable = false;
s.option_add_null_type = false;
s.inline_subschemas = true;
});
let gen = settings.into_generator();
let socket_message = gen.into_root_schema_for::<StaticConfig>();
let generator = settings.into_generator();
let socket_message = generator.into_root_schema_for::<StaticConfig>();
let schema = serde_json::to_string_pretty(&socket_message)?;
println!("{schema}");
}

View File

@@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "KomobarConfig",
"description": "The `komorebi.bar.json` configuration file reference for `v0.1.38`",
"description": "The `komorebi.bar.json` configuration file reference for `v0.1.39`",
"type": "object",
"required": [
"left_widgets",

View File

@@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "StaticConfig",
"description": "The `komorebi.json` static configuration file reference for `v0.1.38`",
"description": "The `komorebi.json` static configuration file reference for `v0.1.39`",
"type": "object",
"properties": {
"animation": {
@@ -1814,6 +1814,10 @@
"columns"
],
"properties": {
"center_focused_column": {
"description": "With an odd number of visible columns, keep the focused window column centered",
"type": "boolean"
},
"columns": {
"description": "Desired number of visible columns (default: 3)",
"type": "integer",