mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-18 09:46:58 +02:00
Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a6905b9e8 | |||
| 18ee667896 | |||
| 1a42c64620 | |||
| b613986474 | |||
| e93751aa5f | |||
| 9872dcd0d7 | |||
| f99c82f5d4 | |||
| b255058467 | |||
| 2abe618354 | |||
| e953715fef | |||
| 2ac1929117 | |||
| e33a5f28f0 | |||
| 15d069f2bf | |||
| 3e9947c2e2 | |||
| 1c23439f95 | |||
| 6eb2905e00 | |||
| 52a745d0a3 | |||
| 80877cc449 | |||
| 160cb7202d | |||
| 8d085df1ba | |||
| 5d48a5c5b4 | |||
| 539aeec965 | |||
| bcfb058bc3 | |||
| e07b464b0d | |||
| ffa76ea28c | |||
| 2c00d79968 | |||
| 78177af6b8 | |||
| 86e0d40828 | |||
| 48f6ac8964 | |||
| 1db572f789 | |||
| 3dad77533b | |||
| f40fb9a251 | |||
| b4e16e43e9 | |||
| 4c2e8ff6d2 | |||
| c879aae1e7 | |||
| 7e87e83189 | |||
| aae9338f66 | |||
| f68a709f1d | |||
| c76846ac63 | |||
| b29dd8b1d1 | |||
| 59c3c14731 | |||
| db96f2cc5a | |||
| a37a6752a8 | |||
| 4d0df9c5b5 | |||
| f8ea62f857 | |||
| 93bb41737b | |||
| 280352eeef | |||
| 7619b9b4ed | |||
| 72a4d5276e | |||
| 4bb3b83d57 | |||
| ccd2f3a464 | |||
| 7e242ada66 | |||
| 5b2acd0f12 | |||
| ec0bbaae98 | |||
| 3c44f3bfdb | |||
| 7839980ddf | |||
| 6416c0b6eb | |||
| f6ccec9505 | |||
| 21cb5e1e6f | |||
| 009c0dcd28 | |||
| 98c5ab3b9b | |||
| d4eeec994f | |||
| e9ed1cfd3b | |||
| 4a2eb391f7 | |||
| 41e18bccc6 | |||
| 3d373b3630 | |||
| b4e61b079c | |||
| eec6312a51 |
@@ -12,7 +12,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check and close feature issues
|
- name: Check and close feature issues
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const issue = context.payload.issue;
|
const issue = context.payload.issue;
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ on:
|
|||||||
- hotfix/*
|
- hotfix/*
|
||||||
tags:
|
tags:
|
||||||
- v*
|
- v*
|
||||||
schedule:
|
# schedule:
|
||||||
- cron: "30 0 * * 0" # Every day at 00:30 UTC
|
# - cron: "30 0 * * 0" # Every day at 00:30 UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo-deny:
|
cargo-deny:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||||
@@ -43,10 +43,11 @@ jobs:
|
|||||||
RUSTFLAGS: -Ctarget-feature=+crt-static -Dwarnings
|
RUSTFLAGS: -Ctarget-feature=+crt-static -Dwarnings
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: rustup toolchain install stable --profile minimal
|
- 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
|
- run: rustup toolchain install nightly --allow-downgrade -c rustfmt
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
@@ -64,7 +65,7 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
cargo install cargo-wix
|
cargo install cargo-wix
|
||||||
cargo wix --no-build -p komorebi --nocapture -I .\wix\main.wxs --target ${{ matrix.platform.target }}
|
cargo wix --no-build -p komorebi --nocapture -I .\wix\main.wxs --target ${{ matrix.platform.target }}
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: komorebi-${{ matrix.platform.target }}-${{ github.sha }}
|
name: komorebi-${{ matrix.platform.target }}-${{ github.sha }}
|
||||||
path: |
|
path: |
|
||||||
@@ -81,12 +82,12 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- shell: bash
|
- shell: bash
|
||||||
run: echo "VERSION=nightly" >> $GITHUB_ENV
|
run: echo "VERSION=nightly" >> $GITHUB_ENV
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v6
|
||||||
- run: |
|
- run: |
|
||||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||||
@@ -128,14 +129,14 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- shell: bash
|
- shell: bash
|
||||||
run: |
|
run: |
|
||||||
TAG=${{ github.event.release.tag_name }}
|
TAG=${{ github.event.release.tag_name }}
|
||||||
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v6
|
||||||
- run: |
|
- run: |
|
||||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||||
@@ -170,14 +171,14 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ github.token }}
|
GH_TOKEN: ${{ github.token }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- shell: bash
|
- shell: bash
|
||||||
run: |
|
run: |
|
||||||
TAG=${{ github.ref_name }}
|
TAG=${{ github.ref_name }}
|
||||||
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v6
|
||||||
- run: |
|
- run: |
|
||||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||||
|
|||||||
Generated
+1706
-1223
File diff suppressed because it is too large
Load Diff
+17
-10
@@ -9,7 +9,7 @@ members = [
|
|||||||
"komorebic-no-console",
|
"komorebic-no-console",
|
||||||
"komorebi-bar",
|
"komorebi-bar",
|
||||||
"komorebi-themes",
|
"komorebi-themes",
|
||||||
"komorebi-shortcuts"
|
"komorebi-shortcuts",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
@@ -19,8 +19,8 @@ chrono = "0.4"
|
|||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
crossbeam-utils = "0.8"
|
crossbeam-utils = "0.8"
|
||||||
color-eyre = "0.6"
|
color-eyre = "0.6"
|
||||||
eframe = "0.31"
|
eframe = "0.32"
|
||||||
egui_extras = "0.31"
|
egui_extras = "0.32"
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
dunce = "1"
|
dunce = "1"
|
||||||
hotwatch = "0.5"
|
hotwatch = "0.5"
|
||||||
@@ -35,18 +35,18 @@ tracing-appender = "0.2"
|
|||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
paste = "1"
|
paste = "1"
|
||||||
sysinfo = "0.34"
|
sysinfo = "0.37"
|
||||||
uds_windows = "1"
|
uds_windows = "1"
|
||||||
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "a28c6559a9de2f92c142a714947a9b081776caca" }
|
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "8c42d8db257d30fe95bc98c2e5cd8f75da861021" }
|
||||||
windows-numerics = { version = "0.2" }
|
windows-numerics = { version = "0.3" }
|
||||||
windows-implement = { version = "0.60" }
|
windows-implement = { version = "0.60" }
|
||||||
windows-interface = { version = "0.59" }
|
windows-interface = { version = "0.59" }
|
||||||
windows-core = { version = "0.61" }
|
windows-core = { version = "0.62" }
|
||||||
shadow-rs = "1"
|
shadow-rs = "1"
|
||||||
which = "7"
|
which = "8"
|
||||||
|
|
||||||
[workspace.dependencies.windows]
|
[workspace.dependencies.windows]
|
||||||
version = "0.61"
|
version = "0.62"
|
||||||
features = [
|
features = [
|
||||||
"Foundation_Numerics",
|
"Foundation_Numerics",
|
||||||
"Win32_Devices",
|
"Win32_Devices",
|
||||||
@@ -73,5 +73,12 @@ features = [
|
|||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
"Win32_System_WindowsProgramming",
|
"Win32_System_WindowsProgramming",
|
||||||
"Media",
|
"Media",
|
||||||
"Media_Control"
|
"Media_Control",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release-opt]
|
||||||
|
inherits = "release"
|
||||||
|
lto = true
|
||||||
|
panic = "abort"
|
||||||
|
codegen-units = 1
|
||||||
|
strip = true
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust c
|
|||||||
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
|
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.37"}
|
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.38"}
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use komorebi_client::Notification;
|
use komorebi_client::Notification;
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ feature-depth = 1
|
|||||||
ignore = [
|
ignore = [
|
||||||
{ id = "RUSTSEC-2020-0016", reason = "local tcp connectivity is an opt-in feature, and there is no upgrade path for TcpStreamExt" },
|
{ id = "RUSTSEC-2020-0016", reason = "local tcp connectivity is an opt-in feature, and there is no upgrade path for TcpStreamExt" },
|
||||||
{ id = "RUSTSEC-2024-0436", reason = "paste being unmaintained is not an issue in our use" },
|
{ id = "RUSTSEC-2024-0436", reason = "paste being unmaintained is not an issue in our use" },
|
||||||
{ id = "RUSTSEC-2024-0320", reason = "not using any yaml features from this library" }
|
{ id = "RUSTSEC-2024-0320", reason = "not using any yaml features from this library" },
|
||||||
|
{ id = "RUSTSEC-2025-0056", reason = "only used for colour palette generation" }
|
||||||
]
|
]
|
||||||
|
|
||||||
[licenses]
|
[licenses]
|
||||||
allow = [
|
allow = [
|
||||||
"0BSD",
|
"0BSD",
|
||||||
"Apache-2.0",
|
"Apache-2.0",
|
||||||
|
"Apache-2.0 WITH LLVM-exception",
|
||||||
"Artistic-2.0",
|
"Artistic-2.0",
|
||||||
"BSD-2-Clause",
|
"BSD-2-Clause",
|
||||||
"BSD-3-Clause",
|
"BSD-3-Clause",
|
||||||
@@ -106,10 +108,10 @@ unknown-git = "deny"
|
|||||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||||
allow-git = [
|
allow-git = [
|
||||||
"https://github.com/LGUG2Z/base16-egui-themes",
|
"https://github.com/LGUG2Z/base16-egui-themes",
|
||||||
"https://github.com/LGUG2Z/catppuccin-egui",
|
|
||||||
"https://github.com/LGUG2Z/windows-icons",
|
"https://github.com/LGUG2Z/windows-icons",
|
||||||
"https://github.com/LGUG2Z/win32-display-data",
|
"https://github.com/LGUG2Z/win32-display-data",
|
||||||
"https://github.com/LGUG2Z/flavours",
|
"https://github.com/LGUG2Z/flavours",
|
||||||
"https://github.com/LGUG2Z/base16_color_scheme",
|
"https://github.com/LGUG2Z/base16_color_scheme",
|
||||||
"https://github.com/LGUG2Z/whkd",
|
"https://github.com/LGUG2Z/whkd",
|
||||||
|
# "https://github.com/LGUG2Z/catppuccin-egui",
|
||||||
]
|
]
|
||||||
|
|||||||
+422
-356
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@ Options:
|
|||||||
Desired ease function for animation
|
Desired ease function for animation
|
||||||
|
|
||||||
[default: linear]
|
[default: linear]
|
||||||
[possible values: linear, ease-in-sine, ease-out-sine, ease-in-out-sine, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-in-out-cubic, ease-in-quart, ease-out-quart, ease-in-out-quart, ease-in-quint, ease-out-quint, ease-in-out-quint,
|
[possible values: linear, ease-in-sine, ease-out-sine, ease-in-out-sine, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-in-out-cubic, ease-in-quart, ease-out-quart, ease-in-out-quart, ease-in-quint, ease-out-quint, ease-in-out-quint, ease-in-expo,
|
||||||
ease-in-expo, ease-out-expo, ease-in-out-expo, ease-in-circ, ease-out-circ, ease-in-out-circ, ease-in-back, ease-out-back, ease-in-out-back, ease-in-elastic, ease-out-elastic, ease-in-out-elastic, ease-in-bounce, ease-out-bounce, ease-in-out-bounce]
|
ease-out-expo, ease-in-out-expo, ease-in-circ, ease-out-circ, ease-in-out-circ, ease-in-back, ease-out-back, ease-in-out-back, ease-in-elastic, ease-out-elastic, ease-in-out-elastic, ease-in-bounce, ease-out-bounce, ease-in-out-bounce]
|
||||||
|
|
||||||
-a, --animation-type <ANIMATION_TYPE>
|
-a, --animation-type <ANIMATION_TYPE>
|
||||||
Animation type to apply the style to. If not specified, sets global style
|
Animation type to apply the style to. If not specified, sets global style
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe change-layout <DEFAULT_LAYOUT>
|
|||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
<DEFAULT_LAYOUT>
|
<DEFAULT_LAYOUT>
|
||||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack, scrolling]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ Options:
|
|||||||
--whkd
|
--whkd
|
||||||
Enable autostart of whkd
|
Enable autostart of whkd
|
||||||
|
|
||||||
--ahk
|
|
||||||
Enable autostart of ahk
|
|
||||||
|
|
||||||
--bar
|
--bar
|
||||||
Enable autostart of komorebi-bar
|
Enable autostart of komorebi-bar
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ Options:
|
|||||||
--whkd
|
--whkd
|
||||||
Kill whkd if it is running as a background process
|
Kill whkd if it is running as a background process
|
||||||
|
|
||||||
--ahk
|
|
||||||
Kill ahk if it is running as a background process
|
|
||||||
|
|
||||||
--bar
|
--bar
|
||||||
Kill komorebi-bar if it is running as a background process
|
Kill komorebi-bar if it is running as a background process
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# license
|
||||||
|
|
||||||
|
```
|
||||||
|
Specify an email associated with an Individual Commercial Use License
|
||||||
|
|
||||||
|
Usage: komorebic.exe license <EMAIL>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<EMAIL>
|
||||||
|
Email address associated with an Individual Commercial Use License
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
@@ -13,7 +13,7 @@ Arguments:
|
|||||||
The number of window containers on-screen required to trigger this layout rule
|
The number of window containers on-screen required to trigger this layout rule
|
||||||
|
|
||||||
<LAYOUT>
|
<LAYOUT>
|
||||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack, scrolling]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Arguments:
|
|||||||
Target workspace name
|
Target workspace name
|
||||||
|
|
||||||
<VALUE>
|
<VALUE>
|
||||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack, scrolling]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# scrolling-layout-columns
|
||||||
|
|
||||||
|
```
|
||||||
|
Set the number of visible columns for the Scrolling layout on the focused workspace
|
||||||
|
|
||||||
|
Usage: komorebic.exe scrolling-layout-columns <COUNT>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<COUNT>
|
||||||
|
Desired number of visible columns
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
@@ -18,9 +18,6 @@ Options:
|
|||||||
--whkd
|
--whkd
|
||||||
Start whkd in a background process
|
Start whkd in a background process
|
||||||
|
|
||||||
--ahk
|
|
||||||
Start autohotkey configuration file
|
|
||||||
|
|
||||||
--bar
|
--bar
|
||||||
Start komorebi-bar in a background process
|
Start komorebi-bar in a background process
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ Options:
|
|||||||
--whkd
|
--whkd
|
||||||
Stop whkd if it is running as a background process
|
Stop whkd if it is running as a background process
|
||||||
|
|
||||||
--ahk
|
|
||||||
Stop ahk if it is running as a background process
|
|
||||||
|
|
||||||
--bar
|
--bar
|
||||||
Stop komorebi-bar if it is running as a background process
|
Stop komorebi-bar if it is running as a background process
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# toggle-pause
|
# toggle-pause
|
||||||
|
|
||||||
```
|
```
|
||||||
Toggle window tiling on the focused workspace
|
Toggle the paused state for all window tiling
|
||||||
|
|
||||||
Usage: komorebic.exe toggle-pause
|
Usage: komorebic.exe toggle-pause
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Arguments:
|
|||||||
The number of window containers on-screen required to trigger this layout rule
|
The number of window containers on-screen required to trigger this layout rule
|
||||||
|
|
||||||
<LAYOUT>
|
<LAYOUT>
|
||||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack, scrolling]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Arguments:
|
|||||||
Workspace index on the specified monitor (zero-indexed)
|
Workspace index on the specified monitor (zero-indexed)
|
||||||
|
|
||||||
<VALUE>
|
<VALUE>
|
||||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack, scrolling]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help
|
-h, --help
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# workspace-work-area-offset
|
||||||
|
|
||||||
|
```
|
||||||
|
Set offsets for a workspace to exclude parts of the work area from tiling
|
||||||
|
|
||||||
|
Usage: komorebic.exe workspace-work-area-offset <MONITOR> <WORKSPACE> <LEFT> <TOP> <RIGHT> <BOTTOM>
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
<MONITOR>
|
||||||
|
Monitor index (zero-indexed)
|
||||||
|
|
||||||
|
<WORKSPACE>
|
||||||
|
Workspace index (zero-indexed)
|
||||||
|
|
||||||
|
<LEFT>
|
||||||
|
Size of the left work area offset (set right to left * 2 to maintain right padding)
|
||||||
|
|
||||||
|
<TOP>
|
||||||
|
Size of the top work area offset (set bottom to the same value to maintain bottom padding)
|
||||||
|
|
||||||
|
<RIGHT>
|
||||||
|
Size of the right work area offset
|
||||||
|
|
||||||
|
<BOTTOM>
|
||||||
|
Size of the bottom work area offset
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h, --help
|
||||||
|
Print help
|
||||||
|
|
||||||
|
```
|
||||||
@@ -16,6 +16,19 @@ the example files have been downloaded. For most new users this will be in the
|
|||||||
komorebic quickstart
|
komorebic quickstart
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Corporate Devices Enrolled in MDM
|
||||||
|
|
||||||
|
If you are using `komorebi` on a corporate device enrolled in mobile device
|
||||||
|
management, you will receive a pop-up when you run `komorebic start` reminding
|
||||||
|
you that the [Komorebi License](https://github.com/LGUG2Z/komorebi-license) does
|
||||||
|
not permit any kind of commercial use.
|
||||||
|
|
||||||
|
You can remove this pop-up by running `komorebic license <email>` with the email
|
||||||
|
associated with your Individual Commercial Use License. A single HTTP request
|
||||||
|
will be sent with the given email address to verify license validity.
|
||||||
|
|
||||||
|
## Starting komorebi
|
||||||
|
|
||||||
With the example configurations downloaded, you can now start `komorebi`,
|
With the example configurations downloaded, you can now start `komorebi`,
|
||||||
`komorebi-bar` and `whkd`.
|
`komorebi-bar` and `whkd`.
|
||||||
|
|
||||||
@@ -23,6 +36,9 @@ With the example configurations downloaded, you can now start `komorebi`,
|
|||||||
komorebic start --whkd --bar
|
komorebic start --whkd --bar
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you don't want to use the komorebi status bar, you can remove the `--bar` option
|
||||||
|
from the above command.
|
||||||
|
|
||||||
## komorebi.json
|
## komorebi.json
|
||||||
|
|
||||||
The example window manager configuration sets some sane defaults and provides
|
The example window manager configuration sets some sane defaults and provides
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.37/schema.bar.json",
|
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.39/schema.bar.json",
|
||||||
"monitor": 0,
|
"monitor": 0,
|
||||||
"font_family": "JetBrains Mono",
|
"font_family": "JetBrains Mono",
|
||||||
"theme": {
|
"theme": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.37/schema.json",
|
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.39/schema.json",
|
||||||
"app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
|
"app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
|
||||||
"window_hiding_behaviour": "Cloak",
|
"window_hiding_behaviour": "Cloak",
|
||||||
"cross_monitor_move_behaviour": "Insert",
|
"cross_monitor_move_behaviour": "Insert",
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ fmt:
|
|||||||
prettier --write .github/FUNDING.yml
|
prettier --write .github/FUNDING.yml
|
||||||
prettier --write .github/workflows/windows.yaml
|
prettier --write .github/workflows/windows.yaml
|
||||||
|
|
||||||
|
fix:
|
||||||
|
cargo clippy --fix --allow-dirty
|
||||||
|
|
||||||
install-targets *targets:
|
install-targets *targets:
|
||||||
"{{ targets }}" -split ' ' | ForEach-Object { just install-target $_ }
|
"{{ targets }}" -split ' ' | ForEach-Object { just install-target $_ }
|
||||||
|
|
||||||
@@ -90,4 +93,5 @@ schemagen:
|
|||||||
mv ./bar-config-docs/schema.bar.html ./bar-config-docs/schema.html
|
mv ./bar-config-docs/schema.bar.html ./bar-config-docs/schema.html
|
||||||
|
|
||||||
depgen:
|
depgen:
|
||||||
|
cargo deny check
|
||||||
cargo deny list --format json | jq 'del(.unlicensed)' > dependencies.json
|
cargo deny list --format json | jq 'del(.unlicensed)' > dependencies.json
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-bar"
|
name = "komorebi-bar"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@@ -17,12 +17,12 @@ crossbeam-channel = { workspace = true }
|
|||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
dunce = { workspace = true }
|
dunce = { workspace = true }
|
||||||
eframe = { workspace = true }
|
eframe = { workspace = true }
|
||||||
egui-phosphor = "0.9"
|
egui-phosphor = "0.10"
|
||||||
font-loader = "0.11"
|
font-loader = "0.11"
|
||||||
hotwatch = { workspace = true }
|
hotwatch = { workspace = true }
|
||||||
image = "0.25"
|
image = "0.25"
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
netdev = "0.34"
|
netdev = "0.36"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
|||||||
+134
-97
@@ -1,27 +1,28 @@
|
|||||||
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::KomobarConfig;
|
||||||
use crate::config::KomobarTheme;
|
use crate::config::KomobarTheme;
|
||||||
use crate::config::MonitorConfigOrIndex;
|
use crate::config::MonitorConfigOrIndex;
|
||||||
use crate::config::Position;
|
use crate::config::Position;
|
||||||
use crate::config::PositionConfig;
|
use crate::config::PositionConfig;
|
||||||
|
use crate::config::get_individual_spacing;
|
||||||
use crate::process_hwnd;
|
use crate::process_hwnd;
|
||||||
use crate::render::Color32Ext;
|
use crate::render::Color32Ext;
|
||||||
use crate::render::Grouping;
|
use crate::render::Grouping;
|
||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::render::RenderExt;
|
use crate::render::RenderExt;
|
||||||
use crate::widgets::komorebi::Komorebi;
|
use crate::widgets::komorebi::Komorebi;
|
||||||
use crate::widgets::komorebi::KomorebiNotificationState;
|
use crate::widgets::komorebi::MonitorInfo;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use crate::widgets::widget::WidgetConfig;
|
use crate::widgets::widget::WidgetConfig;
|
||||||
use crate::KomorebiEvent;
|
use color_eyre::eyre;
|
||||||
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 crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::TryRecvError;
|
use crossbeam_channel::TryRecvError;
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
@@ -47,24 +48,20 @@ use eframe::egui::Visuals;
|
|||||||
use font_loader::system_fonts;
|
use font_loader::system_fonts;
|
||||||
use font_loader::system_fonts::FontPropertyBuilder;
|
use font_loader::system_fonts::FontPropertyBuilder;
|
||||||
use komorebi_client::Colour;
|
use komorebi_client::Colour;
|
||||||
use komorebi_client::KomorebiTheme;
|
|
||||||
use komorebi_client::MonitorNotification;
|
use komorebi_client::MonitorNotification;
|
||||||
use komorebi_client::NotificationEvent;
|
use komorebi_client::NotificationEvent;
|
||||||
use komorebi_client::PathExt;
|
use komorebi_client::PathExt;
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
use komorebi_client::VirtualDesktopNotification;
|
use komorebi_client::VirtualDesktopNotification;
|
||||||
use komorebi_themes::catppuccin_egui;
|
|
||||||
use komorebi_themes::Base16Value;
|
|
||||||
use komorebi_themes::Base16Wrapper;
|
use komorebi_themes::Base16Wrapper;
|
||||||
use komorebi_themes::Catppuccin;
|
use komorebi_themes::Catppuccin;
|
||||||
use komorebi_themes::CatppuccinValue;
|
use komorebi_themes::catppuccin_egui;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::io::Result;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::os::windows::process::CommandExt;
|
use std::os::windows::process::CommandExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -72,8 +69,8 @@ use std::process::ChildStdin;
|
|||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
|
||||||
|
|
||||||
@@ -81,7 +78,7 @@ lazy_static! {
|
|||||||
static ref SESSION_STDIN: Mutex<Option<ChildStdin>> = Mutex::new(None);
|
static ref SESSION_STDIN: Mutex<Option<ChildStdin>> = Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_powershell() -> Result<()> {
|
fn start_powershell() -> eyre::Result<()> {
|
||||||
// found running session, do nothing
|
// found running session, do nothing
|
||||||
if SESSION_STDIN.lock().as_mut().is_some() {
|
if SESSION_STDIN.lock().as_mut().is_some() {
|
||||||
tracing::debug!("PowerShell session already started");
|
tracing::debug!("PowerShell session already started");
|
||||||
@@ -105,17 +102,17 @@ fn start_powershell() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_powershell() -> Result<()> {
|
fn stop_powershell() -> eyre::Result<()> {
|
||||||
tracing::debug!("Stopping PowerShell session");
|
tracing::debug!("Stopping PowerShell session");
|
||||||
|
|
||||||
if let Some(mut session_stdin) = SESSION_STDIN.lock().take() {
|
if let Some(mut session_stdin) = SESSION_STDIN.lock().take() {
|
||||||
if let Err(e) = session_stdin.write_all(b"exit\n") {
|
if let Err(e) = session_stdin.write_all(b"exit\n") {
|
||||||
tracing::error!(error = %e, "failed to write exit command to PowerShell stdin");
|
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() {
|
if let Err(e) = session_stdin.flush() {
|
||||||
tracing::error!(error = %e, "failed to flush PowerShell stdin");
|
tracing::error!(error = %e, "failed to flush PowerShell stdin");
|
||||||
return Err(e);
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("PowerShell session stopped");
|
tracing::debug!("PowerShell session stopped");
|
||||||
@@ -126,25 +123,22 @@ fn stop_powershell() -> Result<()> {
|
|||||||
Ok(())
|
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 Some(session_stdin) = SESSION_STDIN.lock().as_mut() {
|
||||||
if let Err(e) = writeln!(session_stdin, "{}", cmd) {
|
if let Err(e) = writeln!(session_stdin, "{cmd}") {
|
||||||
tracing::error!(error = %e, cmd = cmd, "failed to write command to PowerShell stdin");
|
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() {
|
if let Err(e) = session_stdin.flush() {
|
||||||
tracing::error!(error = %e, "failed to flush PowerShell stdin");
|
tracing::error!(error = %e, "failed to flush PowerShell stdin");
|
||||||
return Err(e);
|
return Err(e.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(Error::new(
|
Err(Error::new(ErrorKind::NotFound, "PowerShell session not started").into())
|
||||||
ErrorKind::NotFound,
|
|
||||||
"PowerShell session not started",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Komobar {
|
pub struct Komobar {
|
||||||
@@ -153,7 +147,7 @@ pub struct Komobar {
|
|||||||
pub disabled: bool,
|
pub disabled: bool,
|
||||||
pub config: KomobarConfig,
|
pub config: KomobarConfig,
|
||||||
pub render_config: Rc<RefCell<RenderConfig>>,
|
pub render_config: Rc<RefCell<RenderConfig>>,
|
||||||
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
pub monitor_info: Option<Rc<RefCell<MonitorInfo>>>,
|
||||||
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
pub center_widgets: Vec<Box<dyn BarWidget>>,
|
pub center_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
@@ -325,8 +319,8 @@ pub fn apply_theme(
|
|||||||
// apply rounding to the widgets
|
// apply rounding to the widgets
|
||||||
if let Some(Grouping::Bar(config) | Grouping::Alignment(config) | Grouping::Widget(config)) =
|
if let Some(Grouping::Bar(config) | Grouping::Alignment(config) | Grouping::Widget(config)) =
|
||||||
&grouping
|
&grouping
|
||||||
|
&& let Some(rounding) = config.rounding
|
||||||
{
|
{
|
||||||
if let Some(rounding) = config.rounding {
|
|
||||||
ctx.style_mut(|style| {
|
ctx.style_mut(|style| {
|
||||||
style.visuals.widgets.noninteractive.corner_radius = rounding.into();
|
style.visuals.widgets.noninteractive.corner_radius = rounding.into();
|
||||||
style.visuals.widgets.inactive.corner_radius = rounding.into();
|
style.visuals.widgets.inactive.corner_radius = rounding.into();
|
||||||
@@ -335,7 +329,6 @@ pub fn apply_theme(
|
|||||||
style.visuals.widgets.open.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
|
// Update RenderConfig's background_color so that widgets will have the new color
|
||||||
render_config.borrow_mut().background_color = *bg_color.borrow();
|
render_config.borrow_mut().background_color = *bg_color.borrow();
|
||||||
@@ -345,7 +338,7 @@ impl Komobar {
|
|||||||
pub fn apply_config(
|
pub fn apply_config(
|
||||||
&mut self,
|
&mut self,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
previous_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
previous_monitor_info: Option<Rc<RefCell<MonitorInfo>>>,
|
||||||
) {
|
) {
|
||||||
MAX_LABEL_WIDTH.store(
|
MAX_LABEL_WIDTH.store(
|
||||||
self.config.max_label_width.unwrap_or(400.0) as i32,
|
self.config.max_label_width.unwrap_or(400.0) as i32,
|
||||||
@@ -374,7 +367,7 @@ impl Komobar {
|
|||||||
self.config.icon_scale,
|
self.config.icon_scale,
|
||||||
));
|
));
|
||||||
|
|
||||||
let mut komorebi_notification_state = previous_notification_state;
|
let mut monitor_info = previous_monitor_info;
|
||||||
let mut komorebi_widgets = Vec::new();
|
let mut komorebi_widgets = Vec::new();
|
||||||
|
|
||||||
for (idx, widget_config) in self.config.left_widgets.iter().enumerate() {
|
for (idx, widget_config) in self.config.left_widgets.iter().enumerate() {
|
||||||
@@ -426,19 +419,18 @@ impl Komobar {
|
|||||||
komorebi_widgets
|
komorebi_widgets
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.for_each(|(mut widget, idx, side)| {
|
.for_each(|(mut widget, idx, side)| {
|
||||||
match komorebi_notification_state {
|
match monitor_info {
|
||||||
None => {
|
None => {
|
||||||
komorebi_notification_state =
|
monitor_info = Some(widget.monitor_info.clone());
|
||||||
Some(widget.komorebi_notification_state.clone());
|
|
||||||
}
|
}
|
||||||
Some(ref previous) => {
|
Some(ref previous) => {
|
||||||
if widget.workspaces.is_some_and(|w| w.enable) {
|
if widget.workspaces.is_some() {
|
||||||
previous.borrow_mut().update_from_config(
|
previous
|
||||||
&widget.komorebi_notification_state.borrow(),
|
.borrow_mut()
|
||||||
);
|
.update_from_self(&widget.monitor_info.borrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
widget.komorebi_notification_state = previous.clone();
|
widget.monitor_info = previous.clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,17 +456,17 @@ impl Komobar {
|
|||||||
MonitorConfigOrIndex::Index(idx) => (*idx, None),
|
MonitorConfigOrIndex::Index(idx) => (*idx, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mapped_state = self.komorebi_notification_state.as_ref().map(|state| {
|
let mapped_info = self.monitor_info.as_ref().map(|info| {
|
||||||
let state = state.borrow();
|
let monitor = info.borrow();
|
||||||
(
|
(
|
||||||
state.monitor_usr_idx_map.get(&usr_monitor_index).copied(),
|
monitor.monitor_usr_idx_map.get(&usr_monitor_index).copied(),
|
||||||
state.mouse_follows_focus,
|
monitor.mouse_follows_focus,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(state) = mapped_state {
|
if let Some(info) = mapped_info {
|
||||||
self.monitor_index = state.0;
|
self.monitor_index = info.0;
|
||||||
self.mouse_follows_focus = state.1;
|
self.mouse_follows_focus = info.1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(monitor_index) = self.monitor_index {
|
if let Some(monitor_index) = self.monitor_index {
|
||||||
@@ -526,11 +518,15 @@ impl Komobar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if self.komorebi_notification_state.is_some() && !self.disabled {
|
} 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;
|
self.disabled = true;
|
||||||
} else {
|
} 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;
|
self.disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,7 +562,7 @@ impl Komobar {
|
|||||||
|
|
||||||
tracing::info!("widget configuration options applied");
|
tracing::info!("widget configuration options applied");
|
||||||
|
|
||||||
self.komorebi_notification_state = komorebi_notification_state;
|
self.monitor_info = monitor_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the `size_rect` field. Returns a bool indicating if the field was changed or not
|
/// Updates the `size_rect` field. Returns a bool indicating if the field was changed or not
|
||||||
@@ -603,7 +599,9 @@ impl Komobar {
|
|||||||
end.x -= margin.left + margin.right;
|
end.x -= margin.left + margin.right;
|
||||||
|
|
||||||
if end.y == 0.0 {
|
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 {
|
self.size_rect = komorebi_client::Rect {
|
||||||
@@ -635,8 +633,7 @@ impl Komobar {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
home.is_dir(),
|
home.is_dir(),
|
||||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory"
|
||||||
home_path
|
|
||||||
);
|
);
|
||||||
|
|
||||||
home
|
home
|
||||||
@@ -650,26 +647,6 @@ impl Komobar {
|
|||||||
match komorebi_client::StaticConfig::read(&config) {
|
match komorebi_client::StaticConfig::read(&config) {
|
||||||
Ok(config) => {
|
Ok(config) => {
|
||||||
if let Some(theme) = config.theme {
|
if let Some(theme) = config.theme {
|
||||||
let stack_accent = match theme {
|
|
||||||
KomorebiTheme::Catppuccin {
|
|
||||||
name, stack_border, ..
|
|
||||||
} => stack_border
|
|
||||||
.unwrap_or(CatppuccinValue::Green)
|
|
||||||
.color32(name.as_theme()),
|
|
||||||
KomorebiTheme::Base16 {
|
|
||||||
name, stack_border, ..
|
|
||||||
} => stack_border
|
|
||||||
.unwrap_or(Base16Value::Base0B)
|
|
||||||
.color32(Base16Wrapper::Base16(name)),
|
|
||||||
KomorebiTheme::Custom {
|
|
||||||
ref colours,
|
|
||||||
stack_border,
|
|
||||||
..
|
|
||||||
} => stack_border
|
|
||||||
.unwrap_or(Base16Value::Base0B)
|
|
||||||
.color32(Base16Wrapper::Custom(colours.clone())),
|
|
||||||
};
|
|
||||||
|
|
||||||
apply_theme(
|
apply_theme(
|
||||||
ctx,
|
ctx,
|
||||||
KomobarTheme::from(theme),
|
KomobarTheme::from(theme),
|
||||||
@@ -679,10 +656,6 @@ impl Komobar {
|
|||||||
bar_grouping,
|
bar_grouping,
|
||||||
self.render_config.clone(),
|
self.render_config.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(state) = &self.komorebi_notification_state {
|
|
||||||
state.borrow_mut().stack_accent = Some(stack_accent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@@ -695,8 +668,8 @@ impl Komobar {
|
|||||||
| Grouping::Alignment(config)
|
| Grouping::Alignment(config)
|
||||||
| Grouping::Widget(config),
|
| Grouping::Widget(config),
|
||||||
) = &bar_grouping
|
) = &bar_grouping
|
||||||
|
&& let Some(rounding) = config.rounding
|
||||||
{
|
{
|
||||||
if let Some(rounding) = config.rounding {
|
|
||||||
ctx.style_mut(|style| {
|
ctx.style_mut(|style| {
|
||||||
style.visuals.widgets.noninteractive.corner_radius =
|
style.visuals.widgets.noninteractive.corner_radius =
|
||||||
rounding.into();
|
rounding.into();
|
||||||
@@ -711,7 +684,6 @@ impl Komobar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cc: &eframe::CreationContext<'_>,
|
cc: &eframe::CreationContext<'_>,
|
||||||
@@ -725,7 +697,7 @@ impl Komobar {
|
|||||||
disabled: false,
|
disabled: false,
|
||||||
config,
|
config,
|
||||||
render_config: Rc::new(RefCell::new(RenderConfig::new())),
|
render_config: Rc::new(RefCell::new(RenderConfig::new())),
|
||||||
komorebi_notification_state: None,
|
monitor_info: None,
|
||||||
left_widgets: vec![],
|
left_widgets: vec![],
|
||||||
center_widgets: vec![],
|
center_widgets: vec![],
|
||||||
right_widgets: vec![],
|
right_widgets: vec![],
|
||||||
@@ -871,12 +843,12 @@ impl eframe::App for Komobar {
|
|||||||
|
|
||||||
if self.scale_factor != ctx.native_pixels_per_point().unwrap_or(1.0) {
|
if self.scale_factor != ctx.native_pixels_per_point().unwrap_or(1.0) {
|
||||||
self.scale_factor = ctx.native_pixels_per_point().unwrap_or(1.0);
|
self.scale_factor = ctx.native_pixels_per_point().unwrap_or(1.0);
|
||||||
self.apply_config(ctx, self.komorebi_notification_state.clone());
|
self.apply_config(ctx, self.monitor_info.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(updated_config) = self.rx_config.try_recv() {
|
if let Ok(updated_config) = self.rx_config.try_recv() {
|
||||||
self.config = updated_config;
|
self.config = updated_config;
|
||||||
self.apply_config(ctx, self.komorebi_notification_state.clone());
|
self.apply_config(ctx, self.monitor_info.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.rx_gui.try_recv() {
|
match self.rx_gui.try_recv() {
|
||||||
@@ -966,9 +938,9 @@ impl eframe::App for Komobar {
|
|||||||
) {
|
) {
|
||||||
let monitor_index = self.monitor_index.expect("should have a monitor index");
|
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;
|
should_apply_config = true;
|
||||||
}
|
}
|
||||||
@@ -979,7 +951,7 @@ impl eframe::App for Komobar {
|
|||||||
|
|
||||||
// Check if monitor coordinates/size has changed
|
// Check if monitor coordinates/size has changed
|
||||||
if let Some(monitor_index) = self.monitor_index {
|
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 top = MONITOR_TOP.load(Ordering::SeqCst);
|
||||||
let left = MONITOR_LEFT.load(Ordering::SeqCst);
|
let left = MONITOR_LEFT.load(Ordering::SeqCst);
|
||||||
let right = MONITOR_RIGHT.load(Ordering::SeqCst);
|
let right = MONITOR_RIGHT.load(Ordering::SeqCst);
|
||||||
@@ -989,25 +961,27 @@ impl eframe::App for Komobar {
|
|||||||
bottom: monitor_size.bottom,
|
bottom: monitor_size.bottom,
|
||||||
right,
|
right,
|
||||||
};
|
};
|
||||||
if *monitor_size != rect {
|
if monitor_size != rect {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"Monitor coordinates/size has changed, storing new coordinates: {:#?}",
|
"Monitor coordinates/size has changed, storing new coordinates: {:#?}",
|
||||||
monitor_size
|
monitor_size
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_monitor_coordinates(monitor_size);
|
self.update_monitor_coordinates(&monitor_size);
|
||||||
|
|
||||||
should_apply_config = true;
|
should_apply_config = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(komorebi_notification_state) = &self.komorebi_notification_state {
|
if let Some(monitor_info) = &self.monitor_info {
|
||||||
komorebi_notification_state
|
monitor_info.borrow_mut().update(
|
||||||
.borrow_mut()
|
|
||||||
.handle_notification(
|
|
||||||
ctx,
|
|
||||||
self.monitor_index,
|
self.monitor_index,
|
||||||
notification,
|
notification.state,
|
||||||
|
self.render_config.borrow().show_all_icons,
|
||||||
|
);
|
||||||
|
handle_notification(
|
||||||
|
ctx,
|
||||||
|
notification.event,
|
||||||
self.bg_color.clone(),
|
self.bg_color.clone(),
|
||||||
self.bg_color_with_alpha.clone(),
|
self.bg_color_with_alpha.clone(),
|
||||||
self.config.transparency_alpha,
|
self.config.transparency_alpha,
|
||||||
@@ -1018,7 +992,7 @@ impl eframe::App for Komobar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if should_apply_config {
|
if should_apply_config {
|
||||||
self.apply_config(ctx, self.komorebi_notification_state.clone());
|
self.apply_config(ctx, self.monitor_info.clone());
|
||||||
|
|
||||||
// Reposition the Bar
|
// Reposition the Bar
|
||||||
self.position_bar();
|
self.position_bar();
|
||||||
@@ -1344,3 +1318,66 @@ pub enum Alignment {
|
|||||||
Center,
|
Center,
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn handle_notification(
|
||||||
|
ctx: &Context,
|
||||||
|
event: komorebi_client::NotificationEvent,
|
||||||
|
bg_color: Rc<RefCell<Color32>>,
|
||||||
|
bg_color_with_alpha: Rc<RefCell<Color32>>,
|
||||||
|
transparency_alpha: Option<u8>,
|
||||||
|
grouping: Option<Grouping>,
|
||||||
|
default_theme: Option<KomobarTheme>,
|
||||||
|
render_config: Rc<RefCell<RenderConfig>>,
|
||||||
|
) {
|
||||||
|
if let NotificationEvent::Socket(message) = event {
|
||||||
|
match message {
|
||||||
|
SocketMessage::ReloadStaticConfiguration(path) => {
|
||||||
|
if let Ok(config) = komorebi_client::StaticConfig::read(&path) {
|
||||||
|
if let Some(theme) = config.theme {
|
||||||
|
apply_theme(
|
||||||
|
ctx,
|
||||||
|
KomobarTheme::from(theme),
|
||||||
|
bg_color.clone(),
|
||||||
|
bg_color_with_alpha.clone(),
|
||||||
|
transparency_alpha,
|
||||||
|
grouping,
|
||||||
|
render_config,
|
||||||
|
);
|
||||||
|
tracing::info!("applied theme from updated komorebi.json");
|
||||||
|
} else if let Some(default_theme) = default_theme {
|
||||||
|
apply_theme(
|
||||||
|
ctx,
|
||||||
|
default_theme,
|
||||||
|
bg_color.clone(),
|
||||||
|
bg_color_with_alpha.clone(),
|
||||||
|
transparency_alpha,
|
||||||
|
grouping,
|
||||||
|
render_config,
|
||||||
|
);
|
||||||
|
tracing::info!(
|
||||||
|
"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"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SocketMessage::Theme(theme) => {
|
||||||
|
apply_theme(
|
||||||
|
ctx,
|
||||||
|
KomobarTheme::from(*theme),
|
||||||
|
bg_color,
|
||||||
|
bg_color_with_alpha.clone(),
|
||||||
|
transparency_alpha,
|
||||||
|
grouping,
|
||||||
|
render_config,
|
||||||
|
);
|
||||||
|
tracing::info!("applied theme from komorebi socket message");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
use crate::DEFAULT_PADDING;
|
||||||
use crate::bar::exec_powershell;
|
use crate::bar::exec_powershell;
|
||||||
use crate::render::Grouping;
|
use crate::render::Grouping;
|
||||||
use crate::widgets::widget::WidgetConfig;
|
use crate::widgets::widget::WidgetConfig;
|
||||||
use crate::DEFAULT_PADDING;
|
|
||||||
use eframe::egui::Pos2;
|
use eframe::egui::Pos2;
|
||||||
use eframe::egui::TextBuffer;
|
use eframe::egui::TextBuffer;
|
||||||
use eframe::egui::Vec2;
|
use eframe::egui::Vec2;
|
||||||
@@ -16,7 +16,7 @@ use std::path::PathBuf;
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.37`
|
/// The `komorebi.bar.json` configuration file reference for `v0.1.39`
|
||||||
pub struct KomobarConfig {
|
pub struct KomobarConfig {
|
||||||
/// Bar height (default: 50)
|
/// Bar height (default: 50)
|
||||||
pub height: Option<f32>,
|
pub height: Option<f32>,
|
||||||
@@ -120,7 +120,9 @@ impl KomobarConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if display {
|
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 (canonical, aliases) in map {
|
||||||
for alias in aliases {
|
for alias in aliases {
|
||||||
if raw.contains(alias) {
|
if raw.contains(alias) {
|
||||||
@@ -332,6 +334,7 @@ pub fn get_individual_spacing(
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
#[serde(untagged)]
|
||||||
pub enum MouseMessage {
|
pub enum MouseMessage {
|
||||||
/// Send a message to the komorebi client.
|
/// Send a message to the komorebi client.
|
||||||
/// By default, a batch of messages are sent in the following order:
|
/// By default, a batch of messages are sent in the following order:
|
||||||
@@ -367,12 +370,10 @@ pub enum MouseMessage {
|
|||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[serde(untagged)]
|
|
||||||
Komorebi(KomorebiMouseMessage),
|
Komorebi(KomorebiMouseMessage),
|
||||||
/// Execute a custom command.
|
/// Execute a custom command.
|
||||||
/// CMD (%variable%), Bash ($variable) and PowerShell ($Env:variable) variables will be resolved.
|
/// CMD (%variable%), Bash ($variable) and PowerShell ($Env:variable) variables will be resolved.
|
||||||
/// Example: `komorebic toggle-pause`
|
/// Example: `komorebic toggle-pause`
|
||||||
#[serde(untagged)]
|
|
||||||
Command(String),
|
Command(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +455,7 @@ impl MouseMessage {
|
|||||||
|
|
||||||
tracing::debug!("Sending messages: {messages:?}");
|
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");
|
tracing::error!("could not send commands");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-18
@@ -15,10 +15,10 @@ use eframe::egui::ViewportBuilder;
|
|||||||
use font_loader::system_fonts;
|
use font_loader::system_fonts;
|
||||||
use hotwatch::EventKind;
|
use hotwatch::EventKind;
|
||||||
use hotwatch::Hotwatch;
|
use hotwatch::Hotwatch;
|
||||||
use komorebi_client::replace_env_in_path;
|
|
||||||
use komorebi_client::PathExt;
|
use komorebi_client::PathExt;
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
use komorebi_client::SubscribeOptions;
|
use komorebi_client::SubscribeOptions;
|
||||||
|
use komorebi_client::replace_env_in_path;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -32,8 +32,8 @@ use windows::Win32::Foundation::HWND;
|
|||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
use windows::Win32::System::Threading::GetCurrentProcessId;
|
use windows::Win32::System::Threading::GetCurrentProcessId;
|
||||||
use windows::Win32::System::Threading::GetCurrentThreadId;
|
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::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||||
|
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows;
|
use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
||||||
use windows_core::BOOL;
|
use windows_core::BOOL;
|
||||||
@@ -103,7 +103,7 @@ fn process_hwnd() -> Option<isize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum KomorebiEvent {
|
pub enum KomorebiEvent {
|
||||||
Notification(komorebi_client::Notification),
|
Notification(Box<komorebi_client::Notification>),
|
||||||
Reconnect,
|
Reconnect,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,14 +114,14 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
if opts.schema {
|
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_nullable = false;
|
||||||
s.option_add_null_type = false;
|
s.option_add_null_type = false;
|
||||||
s.inline_subschemas = true;
|
s.inline_subschemas = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
let gen = settings.into_generator();
|
let generator = settings.into_generator();
|
||||||
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
|
let socket_message = generator.into_root_schema_for::<KomobarConfig>();
|
||||||
let schema = serde_json::to_string_pretty(&socket_message)?;
|
let schema = serde_json::to_string_pretty(&socket_message)?;
|
||||||
|
|
||||||
println!("{schema}");
|
println!("{schema}");
|
||||||
@@ -137,14 +137,18 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||||
|
unsafe {
|
||||||
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
unsafe {
|
||||||
std::env::set_var("RUST_LOG", "info");
|
std::env::set_var("RUST_LOG", "info");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tracing::subscriber::set_global_default(
|
tracing::subscriber::set_global_default(
|
||||||
tracing_subscriber::fmt::Subscriber::builder()
|
tracing_subscriber::fmt::Subscriber::builder()
|
||||||
@@ -159,8 +163,7 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
home.is_dir(),
|
home.is_dir(),
|
||||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory"
|
||||||
home_path
|
|
||||||
);
|
);
|
||||||
|
|
||||||
home
|
home
|
||||||
@@ -231,17 +234,17 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
.map_or(usr_monitor_index, |i| *i);
|
.map_or(usr_monitor_index, |i| *i);
|
||||||
|
|
||||||
MONITOR_RIGHT.store(
|
MONITOR_RIGHT.store(
|
||||||
state.monitors.elements()[monitor_index].size().right,
|
state.monitors.elements()[monitor_index].size.right,
|
||||||
Ordering::SeqCst,
|
Ordering::SeqCst,
|
||||||
);
|
);
|
||||||
|
|
||||||
MONITOR_TOP.store(
|
MONITOR_TOP.store(
|
||||||
state.monitors.elements()[monitor_index].size().top,
|
state.monitors.elements()[monitor_index].size.top,
|
||||||
Ordering::SeqCst,
|
Ordering::SeqCst,
|
||||||
);
|
);
|
||||||
|
|
||||||
MONITOR_LEFT.store(
|
MONITOR_LEFT.store(
|
||||||
state.monitors.elements()[monitor_index].size().left,
|
state.monitors.elements()[monitor_index].size.left,
|
||||||
Ordering::SeqCst,
|
Ordering::SeqCst,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -251,11 +254,11 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
None => {
|
None => {
|
||||||
config.position = Some(PositionConfig {
|
config.position = Some(PositionConfig {
|
||||||
start: Some(Position {
|
start: Some(Position {
|
||||||
x: state.monitors.elements()[monitor_index].size().left as f32,
|
x: state.monitors.elements()[monitor_index].size.left as f32,
|
||||||
y: state.monitors.elements()[monitor_index].size().top as f32,
|
y: state.monitors.elements()[monitor_index].size.top as f32,
|
||||||
}),
|
}),
|
||||||
end: Some(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,
|
y: 50.0,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
@@ -263,14 +266,14 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
Some(ref mut position) => {
|
Some(ref mut position) => {
|
||||||
if position.start.is_none() {
|
if position.start.is_none() {
|
||||||
position.start = Some(Position {
|
position.start = Some(Position {
|
||||||
x: state.monitors.elements()[monitor_index].size().left as f32,
|
x: state.monitors.elements()[monitor_index].size.left as f32,
|
||||||
y: state.monitors.elements()[monitor_index].size().top as f32,
|
y: state.monitors.elements()[monitor_index].size.top as f32,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if position.end.is_none() {
|
if position.end.is_none() {
|
||||||
position.end = Some(Position {
|
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,
|
y: 50.0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -378,7 +381,7 @@ fn main() -> color_eyre::Result<()> {
|
|||||||
Ok(notification) => {
|
Ok(notification) => {
|
||||||
tracing::debug!("received notification from komorebi");
|
tracing::debug!("received notification from komorebi");
|
||||||
|
|
||||||
if let Err(error) = tx_gui.send(KomorebiEvent::Notification(notification)) {
|
if let Err(error) = tx_gui.send(KomorebiEvent::Notification(Box::new(notification))) {
|
||||||
tracing::error!("could not send komorebi notification update to gui thread: {error}")
|
tracing::error!("could not send komorebi notification update to gui thread: {error}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
use crate::AUTO_SELECT_FILL_COLOUR;
|
||||||
|
use crate::AUTO_SELECT_TEXT_COLOUR;
|
||||||
use crate::bar::Alignment;
|
use crate::bar::Alignment;
|
||||||
use crate::config::KomobarConfig;
|
use crate::config::KomobarConfig;
|
||||||
use crate::config::MonitorConfigOrIndex;
|
use crate::config::MonitorConfigOrIndex;
|
||||||
use crate::AUTO_SELECT_FILL_COLOUR;
|
|
||||||
use crate::AUTO_SELECT_TEXT_COLOUR;
|
|
||||||
use eframe::egui::Color32;
|
use eframe::egui::Color32;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::CornerRadius;
|
use eframe::egui::CornerRadius;
|
||||||
@@ -18,9 +18,9 @@ use komorebi_client::Rgb;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
static SHOW_KOMOREBI_LAYOUT_OPTIONS: AtomicUsize = AtomicUsize::new(0);
|
static SHOW_KOMOREBI_LAYOUT_OPTIONS: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use super::ImageIcon;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::vec2;
|
|
||||||
use eframe::egui::Color32;
|
use eframe::egui::Color32;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::CornerRadius;
|
use eframe::egui::CornerRadius;
|
||||||
@@ -17,6 +16,7 @@ use eframe::egui::Stroke;
|
|||||||
use eframe::egui::StrokeKind;
|
use eframe::egui::StrokeKind;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::Vec2;
|
use eframe::egui::Vec2;
|
||||||
|
use eframe::egui::vec2;
|
||||||
use komorebi_client::PathExt;
|
use komorebi_client::PathExt;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use starship_battery::units::ratio::percent;
|
|
||||||
use starship_battery::Manager;
|
use starship_battery::Manager;
|
||||||
use starship_battery::State;
|
use starship_battery::State;
|
||||||
|
use starship_battery::units::ratio::percent;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
@@ -87,8 +87,9 @@ impl Battery {
|
|||||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||||
output = None;
|
output = None;
|
||||||
|
|
||||||
if let Ok(mut batteries) = self.manager.batteries() {
|
if let Ok(mut batteries) = self.manager.batteries()
|
||||||
if let Some(Ok(first)) = batteries.nth(0) {
|
&& let Some(Ok(first)) = batteries.nth(0)
|
||||||
|
{
|
||||||
let percentage = first.state_of_charge().get::<percent>().round() as u8;
|
let percentage = first.state_of_charge().get::<percent>().round() as u8;
|
||||||
|
|
||||||
if percentage == 100 && self.hide_on_full_charge {
|
if percentage == 100 && self.hide_on_full_charge {
|
||||||
@@ -123,7 +124,6 @@ impl Battery {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.last_state.clone_from(&output);
|
self.last_state.clone_from(&output);
|
||||||
self.last_updated = now;
|
self.last_updated = now;
|
||||||
@@ -176,13 +176,11 @@ impl BarWidget for Battery {
|
|||||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
&& let Err(error) = Command::new("cmd.exe")
|
||||||
if let Err(error) = Command::new("cmd.exe")
|
|
||||||
.args(["/C", "start", "ms-settings:batterysaver"])
|
.args(["/C", "start", "ms-settings:batterysaver"])
|
||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{error}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -76,8 +76,8 @@ impl Cpu {
|
|||||||
|
|
||||||
CpuOutput {
|
CpuOutput {
|
||||||
label: match self.label_prefix {
|
label: match self.label_prefix {
|
||||||
LabelPrefix::Text | LabelPrefix::IconAndText => format!("CPU: {}%", used),
|
LabelPrefix::Text | LabelPrefix::IconAndText => format!("CPU: {used}%"),
|
||||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", used),
|
LabelPrefix::None | LabelPrefix::Icon => format!("{used}%"),
|
||||||
},
|
},
|
||||||
selected,
|
selected,
|
||||||
}
|
}
|
||||||
@@ -120,12 +120,10 @@ impl BarWidget for Cpu {
|
|||||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
&& let Err(error) =
|
||||||
if let Err(error) =
|
|
||||||
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{error}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,16 @@ use crate::selected_frame::SelectableFrame;
|
|||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::WidgetText;
|
use eframe::egui::WidgetText;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ impl Date {
|
|||||||
.to_string()
|
.to_string()
|
||||||
.trim()
|
.trim()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
Err(_) => format!("Invalid timezone: {}", timezone),
|
Err(_) => format!("Invalid timezone: {timezone}"),
|
||||||
},
|
},
|
||||||
None => Local::now()
|
None => Local::now()
|
||||||
.format(&self.format.fmt_string())
|
.format(&self.format.fmt_string())
|
||||||
@@ -225,7 +226,7 @@ impl BarWidget for Date {
|
|||||||
if SelectableFrame::new(false)
|
if SelectableFrame::new(false)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.add(
|
ui.add(
|
||||||
Label::new(WidgetText::LayoutJob(layout_job.clone()))
|
Label::new(WidgetText::LayoutJob(Arc::from(layout_job.clone())))
|
||||||
.selectable(false),
|
.selectable(false),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
use crate::config::LabelPrefix;
|
use crate::config::LabelPrefix;
|
||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
use color_eyre::eyre;
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::WidgetText;
|
use eframe::egui::WidgetText;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use windows::Win32::Globalization::LCIDToLocaleName;
|
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.
|
/// - `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
|
/// - `Err(())`: Indicates that the function failed to retrieve the locale name or encountered
|
||||||
/// invalid UTF-16 characters during conversion.
|
/// 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 foreground_window_tid = unsafe { GetWindowThreadProcessId(GetForegroundWindow(), None) };
|
||||||
let lcid = unsafe { GetKeyboardLayout(foreground_window_tid) };
|
let lcid = unsafe { GetKeyboardLayout(foreground_window_tid) };
|
||||||
|
|
||||||
@@ -169,7 +171,10 @@ impl BarWidget for Keyboard {
|
|||||||
);
|
);
|
||||||
|
|
||||||
config.apply_on_widget(true, ui, |ui| {
|
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),
|
||||||
|
)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ use crate::config::DisplayFormat;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::komorebi::KomorebiLayoutConfig;
|
use crate::widgets::komorebi::KomorebiLayoutConfig;
|
||||||
use eframe::egui::vec2;
|
use color_eyre::eyre;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::CornerRadius;
|
use eframe::egui::CornerRadius;
|
||||||
use eframe::egui::FontId;
|
use eframe::egui::FontId;
|
||||||
@@ -13,11 +13,12 @@ use eframe::egui::Stroke;
|
|||||||
use eframe::egui::StrokeKind;
|
use eframe::egui::StrokeKind;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::Vec2;
|
use eframe::egui::Vec2;
|
||||||
|
use eframe::egui::vec2;
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
use serde::de::Error;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Deserializer;
|
use serde::Deserializer;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde::de::Error;
|
||||||
use serde_json::from_str;
|
use serde_json::from_str;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
@@ -34,15 +35,14 @@ pub enum KomorebiLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for 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
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let s: String = String::deserialize(deserializer)?;
|
let s: String = String::deserialize(deserializer)?;
|
||||||
|
|
||||||
// Attempt to deserialize the string as a DefaultLayout
|
// Attempt to deserialize the string as a DefaultLayout
|
||||||
if let Ok(default_layout) =
|
if let Ok(default_layout) = from_str::<komorebi_client::DefaultLayout>(&format!("\"{s}\""))
|
||||||
from_str::<komorebi_client::DefaultLayout>(&format!("\"{}\"", s))
|
|
||||||
{
|
{
|
||||||
return Ok(KomorebiLayout::Default(default_layout));
|
return Ok(KomorebiLayout::Default(default_layout));
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ impl<'de> Deserialize<'de> for KomorebiLayout {
|
|||||||
"Floating" => Ok(KomorebiLayout::Floating),
|
"Floating" => Ok(KomorebiLayout::Floating),
|
||||||
"Paused" => Ok(KomorebiLayout::Paused),
|
"Paused" => Ok(KomorebiLayout::Paused),
|
||||||
"Custom" => Ok(KomorebiLayout::Custom),
|
"Custom" => Ok(KomorebiLayout::Custom),
|
||||||
_ => Err(Error::custom(format!("Invalid layout: {}", s))),
|
_ => Err(Error::custom(format!("Invalid layout: {s}"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,8 +92,8 @@ impl KomorebiLayout {
|
|||||||
fn on_click_option(&mut self, monitor_idx: usize, workspace_idx: Option<usize>) {
|
fn on_click_option(&mut self, monitor_idx: usize, workspace_idx: Option<usize>) {
|
||||||
match self {
|
match self {
|
||||||
KomorebiLayout::Default(option) => {
|
KomorebiLayout::Default(option) => {
|
||||||
if let Some(ws_idx) = workspace_idx {
|
if let Some(ws_idx) = workspace_idx
|
||||||
if komorebi_client::send_message(&SocketMessage::WorkspaceLayout(
|
&& komorebi_client::send_message(&SocketMessage::WorkspaceLayout(
|
||||||
monitor_idx,
|
monitor_idx,
|
||||||
ws_idx,
|
ws_idx,
|
||||||
*option,
|
*option,
|
||||||
@@ -103,7 +103,6 @@ impl KomorebiLayout {
|
|||||||
tracing::error!("could not send message to komorebi: WorkspaceLayout");
|
tracing::error!("could not send message to komorebi: WorkspaceLayout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
KomorebiLayout::Monocle => {
|
KomorebiLayout::Monocle => {
|
||||||
if komorebi_client::send_batch([
|
if komorebi_client::send_batch([
|
||||||
SocketMessage::FocusMonitorAtCursor,
|
SocketMessage::FocusMonitorAtCursor,
|
||||||
@@ -188,6 +187,12 @@ impl KomorebiLayout {
|
|||||||
painter.line_segment([c - vec2(r, 0.0), c + vec2(r, 0.0)], stroke);
|
painter.line_segment([c - vec2(r, 0.0), c + vec2(r, 0.0)], stroke);
|
||||||
painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);
|
painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);
|
||||||
}
|
}
|
||||||
|
// TODO: @CtByte can you think of a nice icon to draw here?
|
||||||
|
komorebi_client::DefaultLayout::Scrolling => {
|
||||||
|
painter.line_segment([c - vec2(r / 2.0, r), c + vec2(-r / 2.0, r)], stroke);
|
||||||
|
painter.line_segment([c - vec2(0.0, r), c + vec2(0.0, r)], stroke);
|
||||||
|
painter.line_segment([c - vec2(-r / 2.0, r), c + vec2(r / 2.0, r)], stroke);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
KomorebiLayout::Monocle => {}
|
KomorebiLayout::Monocle => {}
|
||||||
KomorebiLayout::Floating => {
|
KomorebiLayout::Floating => {
|
||||||
@@ -264,8 +269,7 @@ impl KomorebiLayout {
|
|||||||
show_options = self.on_click(&show_options, monitor_idx, workspace_idx);
|
show_options = self.on_click(&show_options, monitor_idx, workspace_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if show_options {
|
if show_options && let Some(workspace_idx) = workspace_idx {
|
||||||
if let Some(workspace_idx) = workspace_idx {
|
|
||||||
Frame::NONE.show(ui, |ui| {
|
Frame::NONE.show(ui, |ui| {
|
||||||
ui.add(
|
ui.add(
|
||||||
Label::new(egui_phosphor::regular::ARROW_FAT_LINES_RIGHT.to_string())
|
Label::new(egui_phosphor::regular::ARROW_FAT_LINES_RIGHT.to_string())
|
||||||
@@ -280,9 +284,7 @@ impl KomorebiLayout {
|
|||||||
KomorebiLayout::Default(
|
KomorebiLayout::Default(
|
||||||
komorebi_client::DefaultLayout::RightMainVerticalStack,
|
komorebi_client::DefaultLayout::RightMainVerticalStack,
|
||||||
),
|
),
|
||||||
KomorebiLayout::Default(
|
KomorebiLayout::Default(komorebi_client::DefaultLayout::HorizontalStack),
|
||||||
komorebi_client::DefaultLayout::HorizontalStack,
|
|
||||||
),
|
|
||||||
KomorebiLayout::Default(
|
KomorebiLayout::Default(
|
||||||
komorebi_client::DefaultLayout::UltrawideVerticalStack,
|
komorebi_client::DefaultLayout::UltrawideVerticalStack,
|
||||||
),
|
),
|
||||||
@@ -315,7 +317,6 @@ impl KomorebiLayout {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
RenderConfig::store_show_komorebi_layout_options(show_options);
|
RenderConfig::store_show_komorebi_layout_options(show_options);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
|
use crate::MAX_LABEL_WIDTH;
|
||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::ui::CustomUi;
|
use crate::ui::CustomUi;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use crate::MAX_LABEL_WIDTH;
|
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::Vec2;
|
use eframe::egui::Vec2;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
@@ -40,24 +40,25 @@ impl Media {
|
|||||||
enable,
|
enable,
|
||||||
session_manager: GlobalSystemMediaTransportControlsSessionManager::RequestAsync()
|
session_manager: GlobalSystemMediaTransportControlsSessionManager::RequestAsync()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get()
|
.join()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toggle(&self) {
|
pub fn toggle(&self) {
|
||||||
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
if let Ok(session) = self.session_manager.GetCurrentSession()
|
||||||
if let Ok(op) = session.TryTogglePlayPauseAsync() {
|
&& let Ok(op) = session.TryTogglePlayPauseAsync()
|
||||||
op.get().unwrap_or_default();
|
{
|
||||||
}
|
op.join().unwrap_or_default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn output(&mut self) -> String {
|
fn output(&mut self) -> String {
|
||||||
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
if let Ok(session) = self.session_manager.GetCurrentSession()
|
||||||
if let Ok(operation) = session.TryGetMediaPropertiesAsync() {
|
&& let Ok(operation) = session.TryGetMediaPropertiesAsync()
|
||||||
if let Ok(properties) = operation.get() {
|
&& let Ok(properties) = operation.join()
|
||||||
if let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title()) {
|
&& let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title())
|
||||||
|
{
|
||||||
if artist.is_empty() {
|
if artist.is_empty() {
|
||||||
return format!("{title}");
|
return format!("{title}");
|
||||||
}
|
}
|
||||||
@@ -68,9 +69,6 @@ impl Media {
|
|||||||
|
|
||||||
return format!("{artist} - {title}");
|
return format!("{artist} - {title}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -79,9 +79,9 @@ impl Memory {
|
|||||||
MemoryOutput {
|
MemoryOutput {
|
||||||
label: match self.label_prefix {
|
label: match self.label_prefix {
|
||||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||||
format!("RAM: {}%", usage)
|
format!("RAM: {usage}%")
|
||||||
}
|
}
|
||||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", usage),
|
LabelPrefix::None | LabelPrefix::Icon => format!("{usage}%"),
|
||||||
},
|
},
|
||||||
selected,
|
selected,
|
||||||
}
|
}
|
||||||
@@ -124,12 +124,10 @@ impl BarWidget for Memory {
|
|||||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
&& let Err(error) =
|
||||||
if let Err(error) =
|
|
||||||
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{error}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Color32;
|
use eframe::egui::Color32;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -58,6 +58,7 @@ pub struct NetworkSelectConfig {
|
|||||||
|
|
||||||
impl From<NetworkConfig> for Network {
|
impl From<NetworkConfig> for Network {
|
||||||
fn from(value: NetworkConfig) -> Self {
|
fn from(value: NetworkConfig) -> Self {
|
||||||
|
let default_refresh_interval = 10;
|
||||||
let data_refresh_interval = value.data_refresh_interval.unwrap_or(10);
|
let data_refresh_interval = value.data_refresh_interval.unwrap_or(10);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@@ -67,10 +68,14 @@ impl From<NetworkConfig> for Network {
|
|||||||
show_default_interface: value.show_default_interface.unwrap_or(true),
|
show_default_interface: value.show_default_interface.unwrap_or(true),
|
||||||
networks_network_activity: Networks::new_with_refreshed_list(),
|
networks_network_activity: Networks::new_with_refreshed_list(),
|
||||||
default_interface: String::new(),
|
default_interface: String::new(),
|
||||||
|
default_refresh_interval,
|
||||||
data_refresh_interval,
|
data_refresh_interval,
|
||||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
|
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
|
||||||
auto_select: value.auto_select,
|
auto_select: value.auto_select,
|
||||||
activity_left_padding: value.activity_left_padding.unwrap_or_default(),
|
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_total_activity: vec![],
|
||||||
last_state_activity: vec![],
|
last_state_activity: vec![],
|
||||||
last_updated_network_activity: Instant::now()
|
last_updated_network_activity: Instant::now()
|
||||||
@@ -86,10 +91,12 @@ pub struct Network {
|
|||||||
pub show_activity: bool,
|
pub show_activity: bool,
|
||||||
pub show_default_interface: bool,
|
pub show_default_interface: bool,
|
||||||
networks_network_activity: Networks,
|
networks_network_activity: Networks,
|
||||||
|
default_refresh_interval: u64,
|
||||||
data_refresh_interval: u64,
|
data_refresh_interval: u64,
|
||||||
label_prefix: LabelPrefix,
|
label_prefix: LabelPrefix,
|
||||||
auto_select: Option<NetworkSelectConfig>,
|
auto_select: Option<NetworkSelectConfig>,
|
||||||
default_interface: String,
|
default_interface: String,
|
||||||
|
last_updated_default_interface: Instant,
|
||||||
last_state_total_activity: Vec<NetworkReading>,
|
last_state_total_activity: Vec<NetworkReading>,
|
||||||
last_state_activity: Vec<NetworkReading>,
|
last_state_activity: Vec<NetworkReading>,
|
||||||
last_updated_network_activity: Instant,
|
last_updated_network_activity: Instant,
|
||||||
@@ -98,10 +105,18 @@ pub struct Network {
|
|||||||
|
|
||||||
impl Network {
|
impl Network {
|
||||||
fn default_interface(&mut self) {
|
fn default_interface(&mut self) {
|
||||||
if let Ok(interface) = netdev::get_default_interface() {
|
let now = Instant::now();
|
||||||
if let Some(friendly_name) = &interface.friendly_name {
|
|
||||||
|
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.default_interface.clone_from(friendly_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_updated_default_interface = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +131,9 @@ impl Network {
|
|||||||
activity.clear();
|
activity.clear();
|
||||||
total_activity.clear();
|
total_activity.clear();
|
||||||
|
|
||||||
if let Ok(interface) = netdev::get_default_interface() {
|
if let Ok(interface) = netdev::get_default_interface()
|
||||||
if let Some(friendly_name) = &interface.friendly_name {
|
&& let Some(friendly_name) = &interface.friendly_name
|
||||||
|
{
|
||||||
self.default_interface.clone_from(friendly_name);
|
self.default_interface.clone_from(friendly_name);
|
||||||
|
|
||||||
self.networks_network_activity.refresh(true);
|
self.networks_network_activity.refresh(true);
|
||||||
@@ -125,10 +141,8 @@ impl Network {
|
|||||||
for (interface_name, data) in &self.networks_network_activity {
|
for (interface_name, data) in &self.networks_network_activity {
|
||||||
if friendly_name.eq(interface_name) {
|
if friendly_name.eq(interface_name) {
|
||||||
if self.show_activity {
|
if self.show_activity {
|
||||||
let received = Self::to_pretty_bytes(
|
let received =
|
||||||
data.received(),
|
Self::to_pretty_bytes(data.received(), self.data_refresh_interval);
|
||||||
self.data_refresh_interval,
|
|
||||||
);
|
|
||||||
let transmitted = Self::to_pretty_bytes(
|
let transmitted = Self::to_pretty_bytes(
|
||||||
data.transmitted(),
|
data.transmitted(),
|
||||||
self.data_refresh_interval,
|
self.data_refresh_interval,
|
||||||
@@ -142,8 +156,7 @@ impl Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.show_total_activity {
|
if self.show_total_activity {
|
||||||
let total_received =
|
let total_received = Self::to_pretty_bytes(data.total_received(), 1);
|
||||||
Self::to_pretty_bytes(data.total_received(), 1);
|
|
||||||
let total_transmitted =
|
let total_transmitted =
|
||||||
Self::to_pretty_bytes(data.total_transmitted(), 1);
|
Self::to_pretty_bytes(data.total_transmitted(), 1);
|
||||||
|
|
||||||
@@ -156,7 +169,6 @@ impl Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
self.last_state_activity.clone_from(&activity);
|
self.last_state_activity.clone_from(&activity);
|
||||||
self.last_state_total_activity.clone_from(&total_activity);
|
self.last_state_total_activity.clone_from(&total_activity);
|
||||||
@@ -312,10 +324,9 @@ impl Network {
|
|||||||
if SelectableFrame::new_auto(selected, auto_focus_fill)
|
if SelectableFrame::new_auto(selected, auto_focus_fill)
|
||||||
.show(ui, add_contents)
|
.show(ui, add_contents)
|
||||||
.clicked()
|
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,6 +546,6 @@ enum DataUnit {
|
|||||||
|
|
||||||
impl fmt::Display for DataUnit {
|
impl fmt::Display for DataUnit {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?}", self)
|
write!(f, "{self:?}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -25,6 +25,10 @@ pub struct StorageConfig {
|
|||||||
pub data_refresh_interval: Option<u64>,
|
pub data_refresh_interval: Option<u64>,
|
||||||
/// Display label prefix
|
/// Display label prefix
|
||||||
pub label_prefix: Option<LabelPrefix>,
|
pub label_prefix: Option<LabelPrefix>,
|
||||||
|
/// Show disks that are read only. (default: false)
|
||||||
|
pub show_read_only_disks: Option<bool>,
|
||||||
|
/// Show removable disks. (default: true)
|
||||||
|
pub show_removable_disks: Option<bool>,
|
||||||
/// Select when the current percentage is over this value [[1-100]]
|
/// Select when the current percentage is over this value [[1-100]]
|
||||||
pub auto_select_over: Option<u8>,
|
pub auto_select_over: Option<u8>,
|
||||||
/// Hide when the current percentage is under this value [[1-100]]
|
/// Hide when the current percentage is under this value [[1-100]]
|
||||||
@@ -38,6 +42,8 @@ impl From<StorageConfig> for Storage {
|
|||||||
disks: Disks::new_with_refreshed_list(),
|
disks: Disks::new_with_refreshed_list(),
|
||||||
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
||||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||||
|
show_read_only_disks: value.show_read_only_disks.unwrap_or(false),
|
||||||
|
show_removable_disks: value.show_removable_disks.unwrap_or(true),
|
||||||
auto_select_over: value.auto_select_over.map(|o| o.clamp(1, 100)),
|
auto_select_over: value.auto_select_over.map(|o| o.clamp(1, 100)),
|
||||||
auto_hide_under: value.auto_hide_under.map(|o| o.clamp(1, 100)),
|
auto_hide_under: value.auto_hide_under.map(|o| o.clamp(1, 100)),
|
||||||
last_updated: Instant::now(),
|
last_updated: Instant::now(),
|
||||||
@@ -55,6 +61,8 @@ pub struct Storage {
|
|||||||
disks: Disks,
|
disks: Disks,
|
||||||
data_refresh_interval: u64,
|
data_refresh_interval: u64,
|
||||||
label_prefix: LabelPrefix,
|
label_prefix: LabelPrefix,
|
||||||
|
show_read_only_disks: bool,
|
||||||
|
show_removable_disks: bool,
|
||||||
auto_select_over: Option<u8>,
|
auto_select_over: Option<u8>,
|
||||||
auto_hide_under: Option<u8>,
|
auto_hide_under: Option<u8>,
|
||||||
last_updated: Instant,
|
last_updated: Instant,
|
||||||
@@ -71,6 +79,12 @@ impl Storage {
|
|||||||
let mut disks = vec![];
|
let mut disks = vec![];
|
||||||
|
|
||||||
for disk in &self.disks {
|
for disk in &self.disks {
|
||||||
|
if disk.is_read_only() && !self.show_read_only_disks {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if disk.is_removable() && !self.show_removable_disks {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let mount = disk.mount_point();
|
let mount = disk.mount_point();
|
||||||
let total = disk.total_space();
|
let total = disk.total_space();
|
||||||
let available = disk.available_space();
|
let available = disk.available_space();
|
||||||
@@ -87,7 +101,7 @@ impl Storage {
|
|||||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||||
format!("{} {}%", mount.to_string_lossy(), percentage)
|
format!("{} {}%", mount.to_string_lossy(), percentage)
|
||||||
}
|
}
|
||||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", percentage),
|
LabelPrefix::None | LabelPrefix::Icon => format!("{percentage}%"),
|
||||||
},
|
},
|
||||||
selected,
|
selected,
|
||||||
})
|
})
|
||||||
@@ -142,8 +156,7 @@ impl BarWidget for Storage {
|
|||||||
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
if SelectableFrame::new_auto(output.selected, auto_focus_fill)
|
||||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
&& let Err(error) = Command::new("cmd.exe")
|
||||||
if let Err(error) = Command::new("cmd.exe")
|
|
||||||
.args([
|
.args([
|
||||||
"/C",
|
"/C",
|
||||||
"explorer.exe",
|
"explorer.exe",
|
||||||
@@ -151,8 +164,7 @@ impl BarWidget for Storage {
|
|||||||
])
|
])
|
||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{error}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use crate::widgets::widget::BarWidget;
|
|||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use chrono::NaiveTime;
|
use chrono::NaiveTime;
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::CornerRadius;
|
use eframe::egui::CornerRadius;
|
||||||
@@ -16,6 +15,7 @@ use eframe::egui::Stroke;
|
|||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
use eframe::egui::Vec2;
|
use eframe::egui::Vec2;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use eframe::epaint::StrokeKind;
|
use eframe::epaint::StrokeKind;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -209,7 +209,7 @@ impl Time {
|
|||||||
Some(dt.time()),
|
Some(dt.time()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Err(_) => (format!("Invalid timezone: {:?}", timezone), None),
|
Err(_) => (format!("Invalid timezone: {timezone:?}"), None),
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let dt = Local::now();
|
let dt = Local::now();
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ use crate::config::LabelPrefix;
|
|||||||
use crate::render::RenderConfig;
|
use crate::render::RenderConfig;
|
||||||
use crate::selected_frame::SelectableFrame;
|
use crate::selected_frame::SelectableFrame;
|
||||||
use crate::widgets::widget::BarWidget;
|
use crate::widgets::widget::BarWidget;
|
||||||
use eframe::egui::text::LayoutJob;
|
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::TextFormat;
|
use eframe::egui::TextFormat;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
use eframe::egui::text::LayoutJob;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@@ -140,16 +140,14 @@ impl BarWidget for Update {
|
|||||||
if SelectableFrame::new(false)
|
if SelectableFrame::new(false)
|
||||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
&& let Err(error) = Command::new("explorer.exe")
|
||||||
if let Err(error) = Command::new("explorer.exe")
|
|
||||||
.args([format!(
|
.args([format!(
|
||||||
"https://github.com/LGUG2Z/komorebi/releases/v{}",
|
"https://github.com/LGUG2Z/komorebi/releases/v{}",
|
||||||
self.latest_version
|
self.latest_version
|
||||||
)])
|
)])
|
||||||
.spawn()
|
.spawn()
|
||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{error}")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-client"
|
name = "komorebi-client"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
+33
-28
@@ -1,8 +1,29 @@
|
|||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![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::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::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::PerAnimationPrefixConfig;
|
||||||
|
pub use komorebi::animation::prefix::AnimationPrefix;
|
||||||
pub use komorebi::asc::ApplicationSpecificConfiguration;
|
pub use komorebi::asc::ApplicationSpecificConfiguration;
|
||||||
pub use komorebi::border_manager::BorderInfo;
|
pub use komorebi::border_manager::BorderInfo;
|
||||||
pub use komorebi::config_generation::ApplicationConfiguration;
|
pub use komorebi::config_generation::ApplicationConfiguration;
|
||||||
@@ -11,8 +32,6 @@ pub use komorebi::config_generation::IdWithIdentifierAndComment;
|
|||||||
pub use komorebi::config_generation::MatchingRule;
|
pub use komorebi::config_generation::MatchingRule;
|
||||||
pub use komorebi::config_generation::MatchingStrategy;
|
pub use komorebi::config_generation::MatchingStrategy;
|
||||||
pub use komorebi::container::Container;
|
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::AnimationStyle;
|
||||||
pub use komorebi::core::ApplicationIdentifier;
|
pub use komorebi::core::ApplicationIdentifier;
|
||||||
pub use komorebi::core::Arrangement;
|
pub use komorebi::core::Arrangement;
|
||||||
@@ -42,41 +61,24 @@ pub use komorebi::core::StackbarLabel;
|
|||||||
pub use komorebi::core::StackbarMode;
|
pub use komorebi::core::StackbarMode;
|
||||||
pub use komorebi::core::StateQuery;
|
pub use komorebi::core::StateQuery;
|
||||||
pub use komorebi::core::WindowKind;
|
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::Monitor;
|
||||||
pub use komorebi::monitor_reconciliator::MonitorNotification;
|
pub use komorebi::monitor_reconciliator::MonitorNotification;
|
||||||
pub use komorebi::ring::Ring;
|
pub use komorebi::ring::Ring;
|
||||||
|
pub use komorebi::splash;
|
||||||
|
pub use komorebi::state::GlobalState;
|
||||||
|
pub use komorebi::state::State;
|
||||||
pub use komorebi::win32_display_data;
|
pub use komorebi::win32_display_data;
|
||||||
pub use komorebi::window::Window;
|
pub use komorebi::window::Window;
|
||||||
pub use komorebi::window_manager_event::WindowManagerEvent;
|
pub use komorebi::window_manager_event::WindowManagerEvent;
|
||||||
pub use komorebi::workspace::Workspace;
|
pub use komorebi::workspace::Workspace;
|
||||||
pub use komorebi::workspace::WorkspaceGlobals;
|
pub use komorebi::workspace::WorkspaceGlobals;
|
||||||
pub use komorebi::workspace::WorkspaceLayer;
|
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;
|
use komorebi::DATA_DIR;
|
||||||
|
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -94,12 +96,15 @@ pub fn send_message(message: &SocketMessage) -> std::io::Result<()> {
|
|||||||
stream.write_all(serde_json::to_string(message)?.as_bytes())
|
stream.write_all(serde_json::to_string(message)?.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_batch(messages: impl IntoIterator<Item = SocketMessage>) -> std::io::Result<()> {
|
pub fn send_batch<Q>(messages: impl IntoIterator<Item = Q>) -> std::io::Result<()>
|
||||||
|
where
|
||||||
|
Q: Borrow<SocketMessage>,
|
||||||
|
{
|
||||||
let socket = DATA_DIR.join(KOMOREBI);
|
let socket = DATA_DIR.join(KOMOREBI);
|
||||||
let mut stream = UnixStream::connect(socket)?;
|
let mut stream = UnixStream::connect(socket)?;
|
||||||
stream.set_write_timeout(Some(Duration::from_secs(1)))?;
|
stream.set_write_timeout(Some(Duration::from_secs(1)))?;
|
||||||
let msgs = messages.into_iter().fold(String::new(), |mut s, m| {
|
let msgs = messages.into_iter().fold(String::new(), |mut s, m| {
|
||||||
if let Ok(m_str) = serde_json::to_string(&m) {
|
if let Ok(m_str) = serde_json::to_string(m.borrow()) {
|
||||||
s.push_str(&m_str);
|
s.push_str(&m_str);
|
||||||
s.push('\n');
|
s.push('\n');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-gui"
|
name = "komorebi-gui"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -1,9 +1,9 @@
|
|||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use eframe::egui::color_picker::Alpha;
|
|
||||||
use eframe::egui::Color32;
|
use eframe::egui::Color32;
|
||||||
use eframe::egui::ViewportBuilder;
|
use eframe::egui::ViewportBuilder;
|
||||||
|
use eframe::egui::color_picker::Alpha;
|
||||||
use komorebi_client::BorderStyle;
|
use komorebi_client::BorderStyle;
|
||||||
use komorebi_client::Colour;
|
use komorebi_client::Colour;
|
||||||
use komorebi_client::DefaultLayout;
|
use komorebi_client::DefaultLayout;
|
||||||
@@ -78,8 +78,8 @@ impl From<&komorebi_client::Monitor> for MonitorConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
size: *value.size(),
|
size: value.size,
|
||||||
work_area_offset: value.work_area_offset().unwrap_or_default(),
|
work_area_offset: value.work_area_offset.unwrap_or_default(),
|
||||||
workspaces,
|
workspaces,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,22 +95,22 @@ struct WorkspaceConfig {
|
|||||||
|
|
||||||
impl From<&komorebi_client::Workspace> for WorkspaceConfig {
|
impl From<&komorebi_client::Workspace> for WorkspaceConfig {
|
||||||
fn from(value: &komorebi_client::Workspace) -> Self {
|
fn from(value: &komorebi_client::Workspace) -> Self {
|
||||||
let layout = match value.layout() {
|
let layout = match value.layout {
|
||||||
Layout::Default(layout) => *layout,
|
Layout::Default(layout) => layout,
|
||||||
Layout::Custom(_) => DefaultLayout::BSP,
|
Layout::Custom(_) => DefaultLayout::BSP,
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = value
|
let name = value
|
||||||
.name()
|
.name
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.unwrap_or_else(|| random_word::get(random_word::Lang::En).to_string());
|
.unwrap_or_else(|| random_word::get(random_word::Lang::En).to_string());
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
layout,
|
layout,
|
||||||
name,
|
name,
|
||||||
tile: *value.tile(),
|
tile: value.tile,
|
||||||
workspace_padding: value.workspace_padding().unwrap_or(20),
|
workspace_padding: value.workspace_padding.unwrap_or(20),
|
||||||
container_padding: value.container_padding().unwrap_or(20),
|
container_padding: value.container_padding.unwrap_or(20),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,7 +437,7 @@ impl eframe::App for KomorebiGui {
|
|||||||
BorderStyle::Square,
|
BorderStyle::Square,
|
||||||
] {
|
] {
|
||||||
if ui
|
if ui
|
||||||
.add(egui::SelectableLabel::new(
|
.add(egui::Button::selectable(
|
||||||
self.border_config.border_style == option,
|
self.border_config.border_style == option,
|
||||||
option.to_string(),
|
option.to_string(),
|
||||||
))
|
))
|
||||||
@@ -494,7 +494,7 @@ impl eframe::App for KomorebiGui {
|
|||||||
StackbarMode::Always,
|
StackbarMode::Always,
|
||||||
] {
|
] {
|
||||||
if ui
|
if ui
|
||||||
.add(egui::SelectableLabel::new(
|
.add(egui::Button::selectable(
|
||||||
self.stackbar_config.mode == option,
|
self.stackbar_config.mode == option,
|
||||||
option.to_string(),
|
option.to_string(),
|
||||||
))
|
))
|
||||||
@@ -513,7 +513,7 @@ impl eframe::App for KomorebiGui {
|
|||||||
ui.collapsing("Label", |ui| {
|
ui.collapsing("Label", |ui| {
|
||||||
for option in [StackbarLabel::Process, StackbarLabel::Title] {
|
for option in [StackbarLabel::Process, StackbarLabel::Title] {
|
||||||
if ui
|
if ui
|
||||||
.add(egui::SelectableLabel::new(
|
.add(egui::Button::selectable(
|
||||||
self.stackbar_config.label == option,
|
self.stackbar_config.label == option,
|
||||||
option.to_string(),
|
option.to_string(),
|
||||||
))
|
))
|
||||||
@@ -772,7 +772,7 @@ impl eframe::App for KomorebiGui {
|
|||||||
DefaultLayout::Grid,
|
DefaultLayout::Grid,
|
||||||
] {
|
] {
|
||||||
if ui
|
if ui
|
||||||
.add(egui::SelectableLabel::new(
|
.add(egui::Button::selectable(
|
||||||
workspace.layout == option,
|
workspace.layout == option,
|
||||||
option.to_string(),
|
option.to_string(),
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use eframe::egui::ViewportBuilder;
|
use eframe::egui::ViewportBuilder;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use whkd_core::HotkeyBinding;
|
|
||||||
use whkd_core::Whkdrc;
|
use whkd_core::Whkdrc;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -58,16 +57,14 @@ impl eframe::App for Quicklook {
|
|||||||
ui.label("Filter");
|
ui.label("Filter");
|
||||||
ui.add(
|
ui.add(
|
||||||
eframe::egui::text_edit::TextEdit::singleline(&mut self.filter)
|
eframe::egui::text_edit::TextEdit::singleline(&mut self.filter)
|
||||||
|
.hint_text("Filter by command...")
|
||||||
.background_color(ctx.style().visuals.faint_bg_color),
|
.background_color(ctx.style().visuals.faint_bg_color),
|
||||||
);
|
);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.end_row();
|
|
||||||
|
|
||||||
for binding in &whkdrc.bindings {
|
for binding in &whkdrc.bindings {
|
||||||
if is_komorebic_binding(binding) {
|
|
||||||
let keys = binding.keys.join(" + ");
|
let keys = binding.keys.join(" + ");
|
||||||
if self.filter.is_empty()
|
if self.filter.is_empty() || binding.command.contains(&self.filter)
|
||||||
|| binding.command.contains(&self.filter)
|
|
||||||
{
|
{
|
||||||
ui.label(keys);
|
ui.label(keys);
|
||||||
ui.label(&binding.command);
|
ui.label(&binding.command);
|
||||||
@@ -75,7 +72,6 @@ impl eframe::App for Quicklook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -100,7 +96,3 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_komorebic_binding(binding: &HotkeyBinding) -> bool {
|
|
||||||
binding.command.starts_with("komorebic")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi-themes"
|
name = "komorebi-themes"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base16-egui-themes = { git = "https://github.com/LGUG2Z/base16-egui-themes", rev = "96f26c88d83781f234d42222293ec73d23a39ad8" }
|
base16-egui-themes = { git = "https://github.com/LGUG2Z/base16-egui-themes", rev = "c9008bd5cfa288c926e9ea3aa18c92073f9281bd" }
|
||||||
catppuccin-egui = { git = "https://github.com/LGUG2Z/catppuccin-egui", rev = "bdaff30959512c4f7ee7304117076a48633d777f", default-features = false, features = ["egui31"] }
|
catppuccin-egui = { version = "5", default-features = false, features = ["egui32"] }
|
||||||
#catppuccin-egui = { version = "5", default-features = false, features = ["egui30"] }
|
|
||||||
eframe = { workspace = true }
|
eframe = { workspace = true }
|
||||||
schemars = { workspace = true, optional = true }
|
schemars = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use hex_color::HexColor;
|
use hex_color::HexColor;
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
use schemars::gen::SchemaGenerator;
|
use schemars::SchemaGenerator;
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
use schemars::schema::InstanceType;
|
use schemars::schema::InstanceType;
|
||||||
#[cfg(feature = "schemars")]
|
#[cfg(feature = "schemars")]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use crate::Base16ColourPalette;
|
||||||
use crate::colour::Colour;
|
use crate::colour::Colour;
|
||||||
use crate::colour::Hex;
|
use crate::colour::Hex;
|
||||||
use crate::Base16ColourPalette;
|
|
||||||
use hex_color::HexColor;
|
use hex_color::HexColor;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
pub mod colour;
|
pub mod colour;
|
||||||
mod generator;
|
mod generator;
|
||||||
|
|
||||||
pub use generator::generate_base16_palette;
|
|
||||||
pub use generator::ThemeVariant;
|
pub use generator::ThemeVariant;
|
||||||
|
pub use generator::generate_base16_palette;
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
@@ -16,14 +16,14 @@ use strum::IntoEnumIterator;
|
|||||||
use crate::colour::Colour;
|
use crate::colour::Colour;
|
||||||
pub use base16_egui_themes::Base16;
|
pub use base16_egui_themes::Base16;
|
||||||
pub use catppuccin_egui;
|
pub use catppuccin_egui;
|
||||||
use eframe::egui::style::Selection;
|
|
||||||
use eframe::egui::style::WidgetVisuals;
|
|
||||||
use eframe::egui::style::Widgets;
|
|
||||||
pub use eframe::egui::Color32;
|
pub use eframe::egui::Color32;
|
||||||
use eframe::egui::Shadow;
|
use eframe::egui::Shadow;
|
||||||
use eframe::egui::Stroke;
|
use eframe::egui::Stroke;
|
||||||
use eframe::egui::Style;
|
use eframe::egui::Style;
|
||||||
use eframe::egui::Visuals;
|
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;
|
use serde_variant::to_variant_name;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||||
|
|||||||
+7
-4
@@ -1,23 +1,25 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "komorebi"
|
name = "komorebi"
|
||||||
version = "0.1.37"
|
version = "0.1.39"
|
||||||
description = "A tiling window manager for Windows"
|
description = "A tiling window manager for Windows"
|
||||||
repository = "https://github.com/LGUG2Z/komorebi"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
komorebi-themes = { path = "../komorebi-themes" }
|
komorebi-themes = { path = "../komorebi-themes" }
|
||||||
|
|
||||||
|
base64 = "0.21"
|
||||||
bitflags = { version = "2", features = ["serde"] }
|
bitflags = { version = "2", features = ["serde"] }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
|
chrono = { workspace = true }
|
||||||
color-eyre = { workspace = true }
|
color-eyre = { workspace = true }
|
||||||
crossbeam-channel = { workspace = true }
|
crossbeam-channel = { workspace = true }
|
||||||
crossbeam-utils = { workspace = true }
|
crossbeam-utils = { workspace = true }
|
||||||
ctrlc = { version = "3", features = ["termination"] }
|
ctrlc = { version = "3", features = ["termination"] }
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
getset = "0.1"
|
ed25519-dalek = "2"
|
||||||
hotwatch = { workspace = true }
|
hotwatch = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
miow = "0.6"
|
miow = "0.6"
|
||||||
@@ -28,9 +30,10 @@ parking_lot = { workspace = true }
|
|||||||
paste = { workspace = true }
|
paste = { workspace = true }
|
||||||
powershell_script = "1.0"
|
powershell_script = "1.0"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
reqwest = { version = "0.12", features = ["blocking"] }
|
||||||
schemars = { workspace = true, optional = true }
|
schemars = { workspace = true, optional = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true, features = ["preserve_order"] }
|
||||||
serde_yaml = { workspace = true }
|
serde_yaml = { workspace = true }
|
||||||
shadow-rs = { workspace = true }
|
shadow-rs = { workspace = true }
|
||||||
strum = { workspace = true }
|
strum = { workspace = true }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use super::prefix::AnimationPrefix;
|
use super::prefix::AnimationPrefix;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@@ -6,10 +6,10 @@ use std::sync::atomic::Ordering;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use super::RenderDispatcher;
|
|
||||||
use super::ANIMATION_DURATION_GLOBAL;
|
use super::ANIMATION_DURATION_GLOBAL;
|
||||||
use super::ANIMATION_FPS;
|
use super::ANIMATION_FPS;
|
||||||
use super::ANIMATION_MANAGER;
|
use super::ANIMATION_MANAGER;
|
||||||
|
use super::RenderDispatcher;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, PartialEq)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
@@ -55,9 +55,9 @@ impl AnimationEngine {
|
|||||||
|
|
||||||
#[allow(clippy::cast_precision_loss)]
|
#[allow(clippy::cast_precision_loss)]
|
||||||
pub fn animate(
|
pub fn animate(
|
||||||
render_dispatcher: (impl RenderDispatcher + Send + 'static),
|
render_dispatcher: impl RenderDispatcher + Send + 'static,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let animation_key = render_dispatcher.get_animation_key();
|
let animation_key = render_dispatcher.get_animation_key();
|
||||||
if ANIMATION_MANAGER.lock().in_progress(animation_key.as_str()) {
|
if ANIMATION_MANAGER.lock().in_progress(animation_key.as_str()) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::core::Rect;
|
|
||||||
use crate::AnimationStyle;
|
use crate::AnimationStyle;
|
||||||
|
use crate::core::Rect;
|
||||||
|
|
||||||
use super::style::apply_ease_func;
|
use super::style::apply_ease_func;
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ use crate::core::animation::AnimationStyle;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use prefix::AnimationPrefix;
|
use prefix::AnimationPrefix;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ pub enum AnimationPrefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String {
|
pub fn new_animation_key(prefix: AnimationPrefix, key: String) -> String {
|
||||||
format!("{}:{}", prefix, key)
|
format!("{prefix}:{key}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
|
|
||||||
pub trait RenderDispatcher {
|
pub trait RenderDispatcher {
|
||||||
fn get_animation_key(&self) -> String;
|
fn get_animation_key(&self) -> String;
|
||||||
fn pre_render(&self) -> Result<()>;
|
fn pre_render(&self) -> eyre::Result<()>;
|
||||||
fn render(&self, delta: f64) -> Result<()>;
|
fn render(&self, delta: f64) -> eyre::Result<()>;
|
||||||
fn post_render(&self) -> Result<()>;
|
fn post_render(&self) -> eyre::Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,30 @@
|
|||||||
use crate::border_manager::window_kind_colour;
|
use crate::WINDOWS_11;
|
||||||
use crate::border_manager::RenderTarget;
|
use crate::WindowsApi;
|
||||||
use crate::border_manager::WindowKind;
|
|
||||||
use crate::border_manager::BORDER_OFFSET;
|
use crate::border_manager::BORDER_OFFSET;
|
||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
|
use crate::border_manager::RenderTarget;
|
||||||
use crate::border_manager::STYLE;
|
use crate::border_manager::STYLE;
|
||||||
|
use crate::border_manager::WindowKind;
|
||||||
|
use crate::border_manager::window_kind_colour;
|
||||||
use crate::core::BorderStyle;
|
use crate::core::BorderStyle;
|
||||||
use crate::core::Rect;
|
use crate::core::Rect;
|
||||||
use crate::windows_api;
|
use crate::windows_api;
|
||||||
use crate::WindowsApi;
|
|
||||||
use crate::WINDOWS_11;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::sync::LazyLock;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::sync::LazyLock;
|
|
||||||
use windows::Win32::Foundation::FALSE;
|
use windows::Win32::Foundation::FALSE;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
use windows::Win32::Foundation::LRESULT;
|
use windows::Win32::Foundation::LRESULT;
|
||||||
use windows::Win32::Foundation::TRUE;
|
use windows::Win32::Foundation::TRUE;
|
||||||
use windows::Win32::Foundation::WPARAM;
|
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_ALPHA_MODE_PREMULTIPLIED;
|
||||||
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
|
use windows::Win32::Graphics::Direct2D::Common::D2D1_COLOR_F;
|
||||||
use windows::Win32::Graphics::Direct2D::Common::D2D1_PIXEL_FORMAT;
|
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_ANTIALIAS_MODE_PER_PRIMITIVE;
|
||||||
use windows::Win32::Graphics::Direct2D::D2D1_BRUSH_PROPERTIES;
|
use windows::Win32::Graphics::Direct2D::D2D1_BRUSH_PROPERTIES;
|
||||||
use windows::Win32::Graphics::Direct2D::D2D1_FACTORY_TYPE_MULTI_THREADED;
|
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_PROPERTIES;
|
||||||
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
use windows::Win32::Graphics::Direct2D::D2D1_RENDER_TARGET_TYPE_DEFAULT;
|
||||||
use windows::Win32::Graphics::Direct2D::D2D1_ROUNDED_RECT;
|
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_BLURREGION;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_BB_ENABLE;
|
use windows::Win32::Graphics::Dwm::DWM_BB_ENABLE;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_BLURBEHIND;
|
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::Dxgi::Common::DXGI_FORMAT_UNKNOWN;
|
||||||
use windows::Win32::Graphics::Gdi::CreateRectRgn;
|
use windows::Win32::Graphics::Gdi::CreateRectRgn;
|
||||||
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
use windows::Win32::Graphics::Gdi::InvalidateRect;
|
||||||
use windows::Win32::Graphics::Gdi::ValidateRect;
|
use windows::Win32::Graphics::Gdi::ValidateRect;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CREATESTRUCTW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
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_DESTROY;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
|
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GWLP_USERDATA;
|
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::IDC_ARROW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::LoadCursorW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SM_CXVIRTUALSCREEN;
|
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_CREATE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
|
||||||
@@ -102,11 +102,11 @@ pub extern "system" fn border_hwnds(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
|||||||
let hwnds = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
|
let hwnds = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
|
||||||
let hwnd = hwnd.0 as isize;
|
let hwnd = hwnd.0 as isize;
|
||||||
|
|
||||||
if let Ok(class) = WindowsApi::real_window_class_w(hwnd) {
|
if let Ok(class) = WindowsApi::real_window_class_w(hwnd)
|
||||||
if class.starts_with("komoborder") {
|
&& class.starts_with("komoborder")
|
||||||
|
{
|
||||||
hwnds.push(hwnd);
|
hwnds.push(hwnd);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
true.into()
|
true.into()
|
||||||
}
|
}
|
||||||
@@ -392,8 +392,9 @@ impl Border {
|
|||||||
tracing::error!("failed to update border position {error}");
|
tracing::error!("failed to update border position {error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rect.is_same_size_as(&old_rect) {
|
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 Some(render_target) = (*border_pointer).render_target.as_ref()
|
||||||
|
{
|
||||||
let border_width = (*border_pointer).width;
|
let border_width = (*border_pointer).width;
|
||||||
let border_offset = (*border_pointer).offset;
|
let border_offset = (*border_pointer).offset;
|
||||||
|
|
||||||
@@ -450,7 +451,6 @@ impl Border {
|
|||||||
let _ = render_target.EndDraw(None, None);
|
let _ = render_target.EndDraw(None, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT(0)
|
LRESULT(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
mod border;
|
mod border;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use crate::WindowsApi;
|
||||||
use crate::core::BorderImplementation;
|
use crate::core::BorderImplementation;
|
||||||
use crate::core::BorderStyle;
|
use crate::core::BorderStyle;
|
||||||
use crate::core::WindowKind;
|
use crate::core::WindowKind;
|
||||||
@@ -8,10 +10,8 @@ use crate::ring::Ring;
|
|||||||
use crate::windows_api;
|
use crate::windows_api;
|
||||||
use crate::workspace::Workspace;
|
use crate::workspace::Workspace;
|
||||||
use crate::workspace::WorkspaceLayer;
|
use crate::workspace::WorkspaceLayer;
|
||||||
use crate::WindowManager;
|
|
||||||
use crate::WindowsApi;
|
|
||||||
use border::border_hwnds;
|
|
||||||
pub use border::Border;
|
pub use border::Border;
|
||||||
|
use border::border_hwnds;
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
@@ -22,15 +22,15 @@ use lazy_static::lazy_static;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::collections::hash_map::Entry;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::OnceLock;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::AtomicI32;
|
use std::sync::atomic::AtomicI32;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
use windows::Win32::Graphics::Direct2D::ID2D1HwndRenderTarget;
|
use windows::Win32::Graphics::Direct2D::ID2D1HwndRenderTarget;
|
||||||
@@ -170,7 +170,8 @@ fn window_kind_colour(focus_kind: WindowKind) -> u32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone()) {
|
match handle_notifications(wm.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -179,6 +180,7 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
|||||||
tracing::warn!("restarting failed thread: {}", error);
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,9 +211,9 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|w| w.hwnd)
|
.map(|w| w.hwnd)
|
||||||
.collect::<Vec<_>>();
|
.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]
|
[focused_workspace_idx]
|
||||||
.layer();
|
.layer;
|
||||||
let foreground_window = WindowsApi::foreground_window().unwrap_or_default();
|
let foreground_window = WindowsApi::foreground_window().unwrap_or_default();
|
||||||
let layer_changed = previous_layer != workspace_layer;
|
let layer_changed = previous_layer != workspace_layer;
|
||||||
let forced_update = matches!(notification, Notification::ForceUpdate);
|
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
|
// Only operate on the focused workspace of each monitor
|
||||||
if let Some(ws) = m.focused_workspace() {
|
if let Some(ws) = m.focused_workspace() {
|
||||||
// Handle the monocle container separately
|
// 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 {
|
let window_kind = if monitor_idx != focused_monitor_idx {
|
||||||
WindowKind::Unfocused
|
WindowKind::Unfocused
|
||||||
} else {
|
} else {
|
||||||
@@ -237,7 +239,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.set_accent(window_kind_colour(window_kind))?;
|
.set_accent(window_kind_colour(window_kind))?;
|
||||||
|
|
||||||
if ws.layer() == &WorkspaceLayer::Floating {
|
if ws.layer == WorkspaceLayer::Floating {
|
||||||
for window in ws.floating_windows() {
|
for window in ws.floating_windows() {
|
||||||
let mut window_kind = WindowKind::Unfocused;
|
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()
|
let window_kind = if idx != ws.focused_container_idx()
|
||||||
|| monitor_idx != focused_monitor_idx
|
|| monitor_idx != focused_monitor_idx
|
||||||
{
|
{
|
||||||
if ws.locked_containers().contains(&idx) {
|
if c.locked {
|
||||||
WindowKind::UnfocusedLocked
|
WindowKind::UnfocusedLocked
|
||||||
} else {
|
} else {
|
||||||
WindowKind::Unfocused
|
WindowKind::Unfocused
|
||||||
@@ -339,16 +341,12 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
should_process_notification = true;
|
should_process_notification = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !should_process_notification {
|
if !should_process_notification
|
||||||
if let Some(Notification::Update(ref previous)) = previous_notification
|
&& let Some(Notification::Update(ref previous)) = previous_notification
|
||||||
{
|
&& previous.unwrap_or_default() != notification_hwnd.unwrap_or_default()
|
||||||
if previous.unwrap_or_default()
|
|
||||||
!= notification_hwnd.unwrap_or_default()
|
|
||||||
{
|
{
|
||||||
should_process_notification = true;
|
should_process_notification = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
should_process_notification
|
should_process_notification
|
||||||
}
|
}
|
||||||
@@ -356,7 +354,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !should_process_notification {
|
if !should_process_notification {
|
||||||
tracing::trace!("monitor state matches latest snapshot, skipping notification");
|
tracing::debug!("monitor state matches latest snapshot, skipping notification");
|
||||||
continue 'receiver;
|
continue 'receiver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +381,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
// Only operate on the focused workspace of each monitor
|
// Only operate on the focused workspace of each monitor
|
||||||
if let Some(ws) = m.focused_workspace() {
|
if let Some(ws) = m.focused_workspace() {
|
||||||
// Workspaces with tiling disabled don't have borders
|
// Workspaces with tiling disabled don't have borders
|
||||||
if !ws.tile() {
|
if !ws.tile {
|
||||||
// Remove all borders on this monitor
|
// Remove all borders on this monitor
|
||||||
remove_borders(
|
remove_borders(
|
||||||
&mut borders,
|
&mut borders,
|
||||||
@@ -396,16 +394,16 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle the monocle container separately
|
// 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 mut new_border = false;
|
||||||
let focused_window_hwnd =
|
let focused_window_hwnd =
|
||||||
monocle.focused_window().map(|w| w.hwnd).unwrap_or_default();
|
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()) {
|
let border = match borders.entry(id.clone()) {
|
||||||
Entry::Occupied(entry) => entry.into_mut(),
|
Entry::Occupied(entry) => entry.into_mut(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
if let Ok(border) = Border::create(
|
if let Ok(border) = Border::create(
|
||||||
monocle.id(),
|
&monocle.id,
|
||||||
focused_window_hwnd,
|
focused_window_hwnd,
|
||||||
monitor_idx,
|
monitor_idx,
|
||||||
) {
|
) {
|
||||||
@@ -463,7 +461,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
|
|
||||||
let border_hwnd = border.hwnd;
|
let border_hwnd = border.hwnd;
|
||||||
|
|
||||||
if ws.layer() == &WorkspaceLayer::Floating {
|
if ws.layer == WorkspaceLayer::Floating {
|
||||||
handle_floating_borders(
|
handle_floating_borders(
|
||||||
&mut borders,
|
&mut borders,
|
||||||
&mut windows_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_hwnd = WindowsApi::foreground_window().unwrap_or_default();
|
||||||
let foreground_monitor_id =
|
let foreground_monitor_id =
|
||||||
WindowsApi::monitor_from_window(foreground_hwnd);
|
WindowsApi::monitor_from_window(foreground_hwnd);
|
||||||
let is_maximized = foreground_monitor_id == m.id()
|
let is_maximized =
|
||||||
&& WindowsApi::is_zoomed(foreground_hwnd);
|
foreground_monitor_id == m.id && WindowsApi::is_zoomed(foreground_hwnd);
|
||||||
|
|
||||||
if is_maximized {
|
if is_maximized {
|
||||||
// Remove all borders on this monitor
|
// 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
|
let mut container_and_floating_window_ids = ws
|
||||||
.containers()
|
.containers()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.id().clone())
|
.map(|c| c.id.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for w in ws.floating_windows() {
|
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() {
|
'containers: for (idx, c) in ws.containers().iter().enumerate() {
|
||||||
let focused_window_hwnd =
|
let focused_window_hwnd =
|
||||||
c.focused_window().map(|w| w.hwnd).unwrap_or_default();
|
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
|
// Get the border entry for this container from the map or create one
|
||||||
let mut new_border = false;
|
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::Occupied(entry) => entry.into_mut(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
if let Ok(border) =
|
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;
|
new_border = true;
|
||||||
entry.insert(border)
|
entry.insert(border)
|
||||||
@@ -563,7 +561,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
|| monitor_idx != focused_monitor_idx
|
|| monitor_idx != focused_monitor_idx
|
||||||
|| focused_window_hwnd != foreground_window
|
|| focused_window_hwnd != foreground_window
|
||||||
{
|
{
|
||||||
if ws.locked_containers().contains(&idx) {
|
if c.locked {
|
||||||
WindowKind::UnfocusedLocked
|
WindowKind::UnfocusedLocked
|
||||||
} else {
|
} else {
|
||||||
WindowKind::Unfocused
|
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) {
|
let rect = match WindowsApi::window_rect(focused_window_hwnd) {
|
||||||
Ok(rect) => rect,
|
Ok(rect) => rect,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
remove_border(c.id(), &mut borders, &mut windows_borders)?;
|
remove_border(&c.id, &mut borders, &mut windows_borders)?;
|
||||||
continue 'containers;
|
continue 'containers;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,17 +6,17 @@
|
|||||||
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::ops::Deref;
|
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::HWND;
|
||||||
use windows::Win32::Foundation::RECT;
|
use windows::Win32::Foundation::RECT;
|
||||||
use windows::Win32::Foundation::SIZE;
|
use windows::Win32::Foundation::SIZE;
|
||||||
use windows::Win32::UI::Shell::Common::IObjectArray;
|
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;
|
use windows_core::BOOL;
|
||||||
|
|
||||||
type DesktopID = GUID;
|
type DesktopID = GUID;
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ use interfaces::IServiceProvider;
|
|||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
|
|
||||||
use windows::Win32::Foundation::HWND;
|
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::CoCreateInstance;
|
||||||
use windows::Win32::System::Com::CoInitializeEx;
|
use windows::Win32::System::Com::CoInitializeEx;
|
||||||
use windows::Win32::System::Com::CoUninitialize;
|
use windows::Win32::System::Com::CoUninitialize;
|
||||||
use windows::Win32::System::Com::CLSCTX_ALL;
|
|
||||||
use windows::Win32::System::Com::COINIT_MULTITHREADED;
|
|
||||||
use windows_core::Interface;
|
use windows_core::Interface;
|
||||||
|
|
||||||
struct ComInit();
|
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) {
|
pub extern "C" fn SetCloak(hwnd: HWND, cloak_type: u32, flags: i32) {
|
||||||
COM_INIT.with(|_| {
|
COM_INIT.with(|_| {
|
||||||
let provider = get_iservice_provider();
|
let provider = get_iservice_provider();
|
||||||
|
|||||||
+64
-15
@@ -1,18 +1,19 @@
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use getset::Getters;
|
|
||||||
use nanoid::nanoid;
|
use nanoid::nanoid;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::Lockable;
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
#[getset(get = "pub")]
|
pub id: String,
|
||||||
id: String,
|
#[serde(default)]
|
||||||
|
pub locked: bool,
|
||||||
windows: Ring<Window>,
|
windows: Ring<Window>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,23 +23,34 @@ impl Default for Container {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: nanoid!(),
|
id: nanoid!(),
|
||||||
|
locked: false,
|
||||||
windows: Ring::default(),
|
windows: Ring::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Lockable for Container {
|
||||||
|
fn locked(&self) -> bool {
|
||||||
|
self.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_locked(&mut self, locked: bool) -> &mut Self {
|
||||||
|
self.locked = locked;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Container {
|
impl Container {
|
||||||
pub fn hide(&self, omit: Option<isize>) {
|
pub fn hide(&self, omit: Option<isize>) {
|
||||||
for window in self.windows().iter().rev() {
|
for window in self.windows().iter().rev() {
|
||||||
let mut should_hide = omit.is_none();
|
let mut should_hide = omit.is_none();
|
||||||
|
|
||||||
if !should_hide {
|
if !should_hide
|
||||||
if let Some(omit) = omit {
|
&& let Some(omit) = omit
|
||||||
if omit != window.hwnd {
|
&& omit != window.hwnd
|
||||||
|
{
|
||||||
should_hide = true
|
should_hide = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if should_hide {
|
if should_hide {
|
||||||
window.hide();
|
window.hide();
|
||||||
@@ -69,24 +81,24 @@ impl Container {
|
|||||||
|
|
||||||
pub fn hwnd_from_exe(&self, exe: &str) -> Option<isize> {
|
pub fn hwnd_from_exe(&self, exe: &str) -> Option<isize> {
|
||||||
for window in self.windows() {
|
for window in self.windows() {
|
||||||
if let Ok(window_exe) = window.exe() {
|
if let Ok(window_exe) = window.exe()
|
||||||
if exe == window_exe {
|
&& exe == window_exe
|
||||||
|
{
|
||||||
return Option::from(window.hwnd);
|
return Option::from(window.hwnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn idx_from_exe(&self, exe: &str) -> Option<usize> {
|
pub fn idx_from_exe(&self, exe: &str) -> Option<usize> {
|
||||||
for (idx, window) in self.windows().iter().enumerate() {
|
for (idx, window) in self.windows().iter().enumerate() {
|
||||||
if let Ok(window_exe) = window.exe() {
|
if let Ok(window_exe) = window.exe()
|
||||||
if exe == window_exe {
|
&& exe == window_exe
|
||||||
|
{
|
||||||
return Option::from(idx);
|
return Option::from(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -144,6 +156,7 @@ impl Container {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_contains_window() {
|
fn test_contains_window() {
|
||||||
@@ -250,4 +263,40 @@ mod tests {
|
|||||||
// Should return None since window 4 doesn't exist
|
// Should return None since window 4 doesn't exist
|
||||||
assert_eq!(container.idx_for_window(4), None);
|
assert_eq!(container.idx_for_window(4), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deserializes_with_missing_locked_field_defaults_to_false() {
|
||||||
|
let json = r#"{
|
||||||
|
"id": "test-1",
|
||||||
|
"windows": { "elements": [], "focused": 0 }
|
||||||
|
}"#;
|
||||||
|
let container: Container = serde_json::from_str(json).expect("Should deserialize");
|
||||||
|
|
||||||
|
assert!(!container.locked);
|
||||||
|
assert_eq!(container.id, "test-1");
|
||||||
|
assert!(container.windows().is_empty());
|
||||||
|
|
||||||
|
let json = r#"{
|
||||||
|
"id": "test-2",
|
||||||
|
"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.windows(), &[Window::from(5), Window::from(9)]);
|
||||||
|
assert_eq!(container.focused_window_idx(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serializes_and_deserializes() {
|
||||||
|
let mut container = Container::default();
|
||||||
|
container.set_locked(true);
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&container).expect("Should serialize");
|
||||||
|
let deserialized: Container =
|
||||||
|
serde_json::from_str(&serialized).expect("Should deserialize");
|
||||||
|
|
||||||
|
assert!(deserialized.locked);
|
||||||
|
assert_eq!(deserialized.id, container.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
|
|
||||||
use serde::ser::SerializeSeq;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use serde::ser::SerializeSeq;
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,16 @@ use serde::Serialize;
|
|||||||
use strum::Display;
|
use strum::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
use super::custom_layout::Column;
|
|
||||||
use super::custom_layout::ColumnSplit;
|
|
||||||
use super::custom_layout::ColumnSplitWithCapacity;
|
|
||||||
use super::CustomLayout;
|
use super::CustomLayout;
|
||||||
use super::DefaultLayout;
|
use super::DefaultLayout;
|
||||||
use super::Rect;
|
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 {
|
pub trait Arrangement {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn calculate(
|
fn calculate(
|
||||||
&self,
|
&self,
|
||||||
area: &Rect,
|
area: &Rect,
|
||||||
@@ -21,6 +23,9 @@ pub trait Arrangement {
|
|||||||
container_padding: Option<i32>,
|
container_padding: Option<i32>,
|
||||||
layout_flip: Option<Axis>,
|
layout_flip: Option<Axis>,
|
||||||
resize_dimensions: &[Option<Rect>],
|
resize_dimensions: &[Option<Rect>],
|
||||||
|
focused_idx: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
|
latest_layout: &[Rect],
|
||||||
) -> Vec<Rect>;
|
) -> Vec<Rect>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,9 +38,127 @@ impl Arrangement for DefaultLayout {
|
|||||||
container_padding: Option<i32>,
|
container_padding: Option<i32>,
|
||||||
layout_flip: Option<Axis>,
|
layout_flip: Option<Axis>,
|
||||||
resize_dimensions: &[Option<Rect>],
|
resize_dimensions: &[Option<Rect>],
|
||||||
|
focused_idx: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
|
latest_layout: &[Rect],
|
||||||
) -> Vec<Rect> {
|
) -> Vec<Rect> {
|
||||||
let len = usize::from(len);
|
let len = usize::from(len);
|
||||||
let mut dimensions = match self {
|
let mut dimensions = match self {
|
||||||
|
Self::Scrolling => {
|
||||||
|
let column_count = layout_options
|
||||||
|
.and_then(|o| o.scrolling.map(|s| s.columns))
|
||||||
|
.unwrap_or(3);
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
layout_flip,
|
||||||
|
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
||||||
|
) && let 2.. = len
|
||||||
|
{
|
||||||
|
columns_reverse(&mut layouts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// treat >= column_count as scrolling
|
||||||
|
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 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
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// 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,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(
|
Self::BSP => recursive_fibonacci(
|
||||||
0,
|
0,
|
||||||
len,
|
len,
|
||||||
@@ -60,11 +183,10 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 2.. = len
|
||||||
if let 2.. = len {
|
{
|
||||||
columns_reverse(&mut layouts);
|
columns_reverse(&mut layouts);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -85,11 +207,10 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 2.. = len
|
||||||
if let 2.. = len {
|
{
|
||||||
rows_reverse(&mut layouts);
|
rows_reverse(&mut layouts);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -139,8 +260,8 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 2.. = len
|
||||||
if let 2.. = len {
|
{
|
||||||
let (primary, rest) = layouts.split_at_mut(1);
|
let (primary, rest) = layouts.split_at_mut(1);
|
||||||
let primary = &mut primary[0];
|
let primary = &mut primary[0];
|
||||||
|
|
||||||
@@ -149,16 +270,14 @@ impl Arrangement for DefaultLayout {
|
|||||||
}
|
}
|
||||||
primary.left = rest[0].left + rest[0].right;
|
primary.left = rest[0].left + rest[0].right;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 3.. = len
|
||||||
if let 3.. = len {
|
{
|
||||||
rows_reverse(&mut layouts[1..]);
|
rows_reverse(&mut layouts[1..]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -211,8 +330,8 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 2.. = len
|
||||||
if let 2.. = len {
|
{
|
||||||
let (primary, rest) = layouts.split_at_mut(1);
|
let (primary, rest) = layouts.split_at_mut(1);
|
||||||
let primary = &mut primary[0];
|
let primary = &mut primary[0];
|
||||||
|
|
||||||
@@ -221,16 +340,14 @@ impl Arrangement for DefaultLayout {
|
|||||||
rect.left = primary.left + primary.right;
|
rect.left = primary.left + primary.right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 3.. = len
|
||||||
if let 3.. = len {
|
{
|
||||||
rows_reverse(&mut layouts[1..]);
|
rows_reverse(&mut layouts[1..]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -280,8 +397,8 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 2.. = len
|
||||||
if let 2.. = len {
|
{
|
||||||
let (primary, rest) = layouts.split_at_mut(1);
|
let (primary, rest) = layouts.split_at_mut(1);
|
||||||
let primary = &mut primary[0];
|
let primary = &mut primary[0];
|
||||||
|
|
||||||
@@ -290,16 +407,14 @@ impl Arrangement for DefaultLayout {
|
|||||||
}
|
}
|
||||||
primary.top = rest[0].top + rest[0].bottom;
|
primary.top = rest[0].top + rest[0].bottom;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
Some(Axis::Horizontal | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 3.. = len
|
||||||
if let 3.. = len {
|
{
|
||||||
columns_reverse(&mut layouts[1..]);
|
columns_reverse(&mut layouts[1..]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -407,11 +522,10 @@ impl Arrangement for DefaultLayout {
|
|||||||
if matches!(
|
if matches!(
|
||||||
layout_flip,
|
layout_flip,
|
||||||
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
Some(Axis::Vertical | Axis::HorizontalAndVertical)
|
||||||
) {
|
) && let 4.. = len
|
||||||
if let 4.. = len {
|
{
|
||||||
rows_reverse(&mut layouts[2..]);
|
rows_reverse(&mut layouts[2..]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
layouts
|
layouts
|
||||||
}
|
}
|
||||||
@@ -428,14 +542,25 @@ impl Arrangement for DefaultLayout {
|
|||||||
|
|
||||||
let len = len as i32;
|
let len = len as i32;
|
||||||
|
|
||||||
let num_cols = (len as f32).sqrt().ceil() as i32;
|
let row_constraint = layout_options.and_then(|o| o.grid.map(|g| g.rows));
|
||||||
|
let num_cols = if let Some(rows) = row_constraint {
|
||||||
|
((len as f32) / (rows as f32)).ceil() as i32
|
||||||
|
} else {
|
||||||
|
(len as f32).sqrt().ceil() as i32
|
||||||
|
};
|
||||||
|
|
||||||
let mut iter = layouts.iter_mut().enumerate().peekable();
|
let mut iter = layouts.iter_mut().enumerate().peekable();
|
||||||
|
|
||||||
for col in 0..num_cols {
|
for col in 0..num_cols {
|
||||||
let iter_peek = iter.peek().map(|x| x.0).unwrap_or_default() as i32;
|
let iter_peek = iter.peek().map(|x| x.0).unwrap_or_default() as i32;
|
||||||
let remaining_windows = len - iter_peek;
|
let remaining_windows = len - iter_peek;
|
||||||
let remaining_columns = num_cols - col;
|
let remaining_columns = num_cols - col;
|
||||||
let num_rows_in_this_col = remaining_windows / remaining_columns;
|
|
||||||
|
let num_rows_in_this_col = if let Some(rows) = row_constraint {
|
||||||
|
(remaining_windows / remaining_columns).min(rows as i32)
|
||||||
|
} else {
|
||||||
|
remaining_windows / remaining_columns
|
||||||
|
};
|
||||||
|
|
||||||
let win_height = area.bottom / num_rows_in_this_col;
|
let win_height = area.bottom / num_rows_in_this_col;
|
||||||
let win_width = area.right / num_cols;
|
let win_width = area.right / num_cols;
|
||||||
@@ -487,6 +612,9 @@ impl Arrangement for CustomLayout {
|
|||||||
container_padding: Option<i32>,
|
container_padding: Option<i32>,
|
||||||
_layout_flip: Option<Axis>,
|
_layout_flip: Option<Axis>,
|
||||||
_resize_dimensions: &[Option<Rect>],
|
_resize_dimensions: &[Option<Rect>],
|
||||||
|
_focused_idx: usize,
|
||||||
|
_layout_options: Option<LayoutOptions>,
|
||||||
|
_latest_layout: &[Rect],
|
||||||
) -> Vec<Rect> {
|
) -> Vec<Rect> {
|
||||||
let mut dimensions = vec![];
|
let mut dimensions = vec![];
|
||||||
let container_count = len.get();
|
let container_count = len.get();
|
||||||
@@ -541,7 +669,7 @@ impl Arrangement for CustomLayout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match column {
|
match column {
|
||||||
Column::Primary(Option::Some(_)) => {
|
Column::Primary(Some(_)) => {
|
||||||
let main_column_area = if idx == 0 {
|
let main_column_area = if idx == 0 {
|
||||||
Self::main_column_area(area, primary_right, None)
|
Self::main_column_area(area, primary_right, None)
|
||||||
} else {
|
} else {
|
||||||
@@ -673,8 +801,9 @@ fn calculate_resize_adjustments(resize_dimensions: &[Option<Rect>]) -> Vec<Optio
|
|||||||
|
|
||||||
// This needs to be aware of layout flips
|
// This needs to be aware of layout flips
|
||||||
for (i, opt) in resize_dimensions.iter().enumerate() {
|
for (i, opt) in resize_dimensions.iter().enumerate() {
|
||||||
if let Some(resize_ref) = opt {
|
if let Some(resize_ref) = opt
|
||||||
if i > 0 {
|
&& i > 0
|
||||||
|
{
|
||||||
if resize_ref.left != 0 {
|
if resize_ref.left != 0 {
|
||||||
#[allow(clippy::if_not_else)]
|
#[allow(clippy::if_not_else)]
|
||||||
let range = if i == 1 {
|
let range = if i == 1 {
|
||||||
@@ -737,7 +866,6 @@ fn calculate_resize_adjustments(resize_dimensions: &[Option<Rect>]) -> Vec<Optio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let cleaned_resize_adjustments: Vec<_> = resize_adjustments
|
let cleaned_resize_adjustments: Vec<_> = resize_adjustments
|
||||||
.iter()
|
.iter()
|
||||||
@@ -818,7 +946,7 @@ fn recursive_fibonacci(
|
|||||||
right: resized.right,
|
right: resized.right,
|
||||||
bottom: resized.bottom,
|
bottom: resized.bottom,
|
||||||
}]
|
}]
|
||||||
} else if idx % 2 != 0 {
|
} else if !idx.is_multiple_of(2) {
|
||||||
let mut res = vec![Rect {
|
let mut res = vec![Rect {
|
||||||
left: resized.left,
|
left: resized.left,
|
||||||
top: main_y,
|
top: main_y,
|
||||||
@@ -1115,6 +1243,37 @@ fn calculate_ultrawide_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rec
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calculate_scrolling_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
|
||||||
|
let len = resize_dimensions.len();
|
||||||
|
let mut result = vec![Rect::default(); len];
|
||||||
|
|
||||||
|
if len <= 1 {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, rect) in resize_dimensions.iter().enumerate() {
|
||||||
|
if let Some(rect) = rect {
|
||||||
|
let is_leftmost = i == 0;
|
||||||
|
let is_rightmost = i == len - 1;
|
||||||
|
|
||||||
|
resize_left(&mut result[i], rect.left);
|
||||||
|
resize_right(&mut result[i], rect.right);
|
||||||
|
resize_top(&mut result[i], rect.top);
|
||||||
|
resize_bottom(&mut result[i], rect.bottom);
|
||||||
|
|
||||||
|
if !is_leftmost && rect.left != 0 {
|
||||||
|
resize_right(&mut result[i - 1], rect.left);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_rightmost && rect.right != 0 {
|
||||||
|
resize_left(&mut result[i + 1], rect.right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
fn resize_left(rect: &mut Rect, resize: i32) {
|
fn resize_left(rect: &mut Rect, resize: i32) {
|
||||||
rect.left += resize / 2;
|
rect.left += resize / 2;
|
||||||
rect.right += -resize / 2;
|
rect.right += -resize / 2;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::config_generation::ApplicationConfiguration;
|
use crate::config_generation::ApplicationConfiguration;
|
||||||
use crate::config_generation::ApplicationOptions;
|
use crate::config_generation::ApplicationOptions;
|
||||||
use crate::config_generation::MatchingRule;
|
use crate::config_generation::MatchingRule;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@@ -36,12 +36,12 @@ impl DerefMut for ApplicationSpecificConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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)?;
|
let content = std::fs::read_to_string(pathbuf)?;
|
||||||
Ok(serde_json::from_str(&content)?)
|
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)?)?)
|
Ok(serde_json::to_string_pretty(&Self::load(pathbuf)?)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
@@ -142,11 +142,11 @@ impl ApplicationConfiguration {
|
|||||||
pub struct ApplicationConfigurationGenerator;
|
pub struct ApplicationConfigurationGenerator;
|
||||||
|
|
||||||
impl 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)?)
|
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)?;
|
let mut cfgen = Self::load(content)?;
|
||||||
for cfg in &mut cfgen {
|
for cfg in &mut cfgen {
|
||||||
cfg.populate_default_matching_strategies();
|
cfg.populate_default_matching_strategies();
|
||||||
@@ -156,7 +156,10 @@ impl ApplicationConfigurationGenerator {
|
|||||||
Ok(serde_yaml::to_string(&cfgen)?)
|
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 base_cfgen = Self::load(base_content)?;
|
||||||
let override_cfgen = Self::load(override_content)?;
|
let override_cfgen = Self::load(override_content)?;
|
||||||
|
|
||||||
@@ -182,7 +185,7 @@ impl ApplicationConfigurationGenerator {
|
|||||||
pub fn generate_pwsh(
|
pub fn generate_pwsh(
|
||||||
base_content: &str,
|
base_content: &str,
|
||||||
override_content: Option<&str>,
|
override_content: Option<&str>,
|
||||||
) -> Result<Vec<String>> {
|
) -> eyre::Result<Vec<String>> {
|
||||||
let mut cfgen = if let Some(override_content) = override_content {
|
let mut cfgen = if let Some(override_content) = override_content {
|
||||||
Self::merge(base_content, override_content)?
|
Self::merge(base_content, override_content)?
|
||||||
} else {
|
} else {
|
||||||
@@ -233,7 +236,10 @@ impl ApplicationConfigurationGenerator {
|
|||||||
Ok(lines)
|
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 {
|
let mut cfgen = if let Some(override_content) = override_content {
|
||||||
Self::merge(base_content, override_content)?
|
Self::merge(base_content, override_content)?
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
@@ -5,12 +9,6 @@ use std::ops::Deref;
|
|||||||
use std::ops::DerefMut;
|
use std::ops::DerefMut;
|
||||||
use std::path::Path;
|
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;
|
use super::Rect;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
@@ -32,7 +30,7 @@ impl DerefMut for CustomLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 path = path.as_ref();
|
||||||
let layout: Self = match path.extension() {
|
let layout: Self = match path.extension() {
|
||||||
Some(extension) if extension == "yaml" || extension == "yml" => {
|
Some(extension) if extension == "yaml" || extension == "yml" => {
|
||||||
@@ -41,7 +39,7 @@ impl CustomLayout {
|
|||||||
Some(extension) if extension == "json" => {
|
Some(extension) if extension == "json" => {
|
||||||
serde_json::from_reader(BufReader::new(File::open(path)?))?
|
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() {
|
if !layout.is_valid() {
|
||||||
|
|||||||
@@ -21,9 +21,35 @@ pub enum DefaultLayout {
|
|||||||
UltrawideVerticalStack,
|
UltrawideVerticalStack,
|
||||||
Grid,
|
Grid,
|
||||||
RightMainVerticalStack,
|
RightMainVerticalStack,
|
||||||
|
Scrolling,
|
||||||
// NOTE: If any new layout is added, please make sure to register the same in `DefaultLayout::cycle`
|
// NOTE: If any new layout is added, please make sure to register the same in `DefaultLayout::cycle`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
pub struct LayoutOptions {
|
||||||
|
/// Options related to the Scrolling layout
|
||||||
|
pub scrolling: Option<ScrollingLayoutOptions>,
|
||||||
|
/// Options related to the Grid layout
|
||||||
|
pub grid: Option<GridLayoutOptions>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
pub struct GridLayoutOptions {
|
||||||
|
/// Maximum number of rows per grid column
|
||||||
|
pub rows: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl DefaultLayout {
|
impl DefaultLayout {
|
||||||
pub fn leftmost_index(&self, len: usize) -> usize {
|
pub fn leftmost_index(&self, len: usize) -> usize {
|
||||||
match self {
|
match self {
|
||||||
@@ -31,6 +57,7 @@ impl DefaultLayout {
|
|||||||
n if n > 1 => 1,
|
n if n > 1 => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
},
|
},
|
||||||
|
Self::Scrolling => 0,
|
||||||
DefaultLayout::BSP
|
DefaultLayout::BSP
|
||||||
| DefaultLayout::Columns
|
| DefaultLayout::Columns
|
||||||
| DefaultLayout::Rows
|
| DefaultLayout::Rows
|
||||||
@@ -53,6 +80,7 @@ impl DefaultLayout {
|
|||||||
_ => len.saturating_sub(1),
|
_ => len.saturating_sub(1),
|
||||||
},
|
},
|
||||||
DefaultLayout::RightMainVerticalStack => 0,
|
DefaultLayout::RightMainVerticalStack => 0,
|
||||||
|
DefaultLayout::Scrolling => len.saturating_sub(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +103,7 @@ impl DefaultLayout {
|
|||||||
| Self::RightMainVerticalStack
|
| Self::RightMainVerticalStack
|
||||||
| Self::HorizontalStack
|
| Self::HorizontalStack
|
||||||
| Self::UltrawideVerticalStack
|
| Self::UltrawideVerticalStack
|
||||||
|
| Self::Scrolling
|
||||||
) {
|
) {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
@@ -169,13 +198,15 @@ impl DefaultLayout {
|
|||||||
Self::HorizontalStack => Self::UltrawideVerticalStack,
|
Self::HorizontalStack => Self::UltrawideVerticalStack,
|
||||||
Self::UltrawideVerticalStack => Self::Grid,
|
Self::UltrawideVerticalStack => Self::Grid,
|
||||||
Self::Grid => Self::RightMainVerticalStack,
|
Self::Grid => Self::RightMainVerticalStack,
|
||||||
Self::RightMainVerticalStack => Self::BSP,
|
Self::RightMainVerticalStack => Self::Scrolling,
|
||||||
|
Self::Scrolling => Self::BSP,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn cycle_previous(self) -> Self {
|
pub const fn cycle_previous(self) -> Self {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Scrolling => Self::RightMainVerticalStack,
|
||||||
Self::RightMainVerticalStack => Self::Grid,
|
Self::RightMainVerticalStack => Self::Grid,
|
||||||
Self::Grid => Self::UltrawideVerticalStack,
|
Self::Grid => Self::UltrawideVerticalStack,
|
||||||
Self::UltrawideVerticalStack => Self::HorizontalStack,
|
Self::UltrawideVerticalStack => Self::HorizontalStack,
|
||||||
|
|||||||
+116
-42
@@ -1,9 +1,10 @@
|
|||||||
|
use super::DefaultLayout;
|
||||||
|
use super::OperationDirection;
|
||||||
use super::custom_layout::Column;
|
use super::custom_layout::Column;
|
||||||
use super::custom_layout::ColumnSplit;
|
use super::custom_layout::ColumnSplit;
|
||||||
use super::custom_layout::ColumnSplitWithCapacity;
|
use super::custom_layout::ColumnSplitWithCapacity;
|
||||||
use super::custom_layout::CustomLayout;
|
use super::custom_layout::CustomLayout;
|
||||||
use super::DefaultLayout;
|
use crate::default_layout::LayoutOptions;
|
||||||
use super::OperationDirection;
|
|
||||||
|
|
||||||
pub trait Direction {
|
pub trait Direction {
|
||||||
fn index_in_direction(
|
fn index_in_direction(
|
||||||
@@ -11,6 +12,7 @@ pub trait Direction {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> Option<usize>;
|
) -> Option<usize>;
|
||||||
|
|
||||||
fn is_valid_direction(
|
fn is_valid_direction(
|
||||||
@@ -18,30 +20,35 @@ pub trait Direction {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
fn up_index(
|
fn up_index(
|
||||||
&self,
|
&self,
|
||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
fn down_index(
|
fn down_index(
|
||||||
&self,
|
&self,
|
||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
fn left_index(
|
fn left_index(
|
||||||
&self,
|
&self,
|
||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
fn right_index(
|
fn right_index(
|
||||||
&self,
|
&self,
|
||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize;
|
) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,32 +58,53 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
match op_direction {
|
match op_direction {
|
||||||
OperationDirection::Left => {
|
OperationDirection::Left => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.left_index(Some(op_direction), idx, Some(count)))
|
Option::from(self.left_index(
|
||||||
|
Some(op_direction),
|
||||||
|
idx,
|
||||||
|
Some(count),
|
||||||
|
layout_options,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Right => {
|
OperationDirection::Right => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.right_index(Some(op_direction), idx, Some(count)))
|
Option::from(self.right_index(
|
||||||
|
Some(op_direction),
|
||||||
|
idx,
|
||||||
|
Some(count),
|
||||||
|
layout_options,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Up => {
|
OperationDirection::Up => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.up_index(Some(op_direction), idx, Some(count)))
|
Option::from(self.up_index(
|
||||||
|
Some(op_direction),
|
||||||
|
idx,
|
||||||
|
Some(count),
|
||||||
|
layout_options,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Down => {
|
OperationDirection::Down => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.down_index(Some(op_direction), idx, Some(count)))
|
Option::from(self.down_index(
|
||||||
|
Some(op_direction),
|
||||||
|
idx,
|
||||||
|
Some(count),
|
||||||
|
layout_options,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -89,6 +117,7 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if count < 2 {
|
if count < 2 {
|
||||||
return false;
|
return false;
|
||||||
@@ -101,16 +130,18 @@ impl Direction for DefaultLayout {
|
|||||||
Self::Rows | Self::HorizontalStack => idx != 0,
|
Self::Rows | Self::HorizontalStack => idx != 0,
|
||||||
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != 1,
|
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != 1,
|
||||||
Self::UltrawideVerticalStack => idx > 2,
|
Self::UltrawideVerticalStack => idx > 2,
|
||||||
Self::Grid => !is_grid_edge(op_direction, idx, count),
|
Self::Grid => !is_grid_edge(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => false,
|
||||||
},
|
},
|
||||||
OperationDirection::Down => match self {
|
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::Columns => false,
|
||||||
Self::Rows => idx != count - 1,
|
Self::Rows => idx != count - 1,
|
||||||
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != count - 1,
|
Self::VerticalStack | Self::RightMainVerticalStack => idx != 0 && idx != count - 1,
|
||||||
Self::HorizontalStack => idx == 0,
|
Self::HorizontalStack => idx == 0,
|
||||||
Self::UltrawideVerticalStack => idx > 1 && idx != count - 1,
|
Self::UltrawideVerticalStack => idx > 1 && idx != count - 1,
|
||||||
Self::Grid => !is_grid_edge(op_direction, idx, count),
|
Self::Grid => !is_grid_edge(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => false,
|
||||||
},
|
},
|
||||||
OperationDirection::Left => match self {
|
OperationDirection::Left => match self {
|
||||||
Self::BSP => idx != 0,
|
Self::BSP => idx != 0,
|
||||||
@@ -119,10 +150,11 @@ impl Direction for DefaultLayout {
|
|||||||
Self::Rows => false,
|
Self::Rows => false,
|
||||||
Self::HorizontalStack => idx != 0 && idx != 1,
|
Self::HorizontalStack => idx != 0 && idx != 1,
|
||||||
Self::UltrawideVerticalStack => idx != 1,
|
Self::UltrawideVerticalStack => idx != 1,
|
||||||
Self::Grid => !is_grid_edge(op_direction, idx, count),
|
Self::Grid => !is_grid_edge(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => idx != 0,
|
||||||
},
|
},
|
||||||
OperationDirection::Right => match self {
|
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::Columns => idx != count - 1,
|
||||||
Self::Rows => false,
|
Self::Rows => false,
|
||||||
Self::VerticalStack => idx == 0,
|
Self::VerticalStack => idx == 0,
|
||||||
@@ -132,7 +164,8 @@ impl Direction for DefaultLayout {
|
|||||||
2 => idx != 0,
|
2 => idx != 0,
|
||||||
_ => idx < 2,
|
_ => idx < 2,
|
||||||
},
|
},
|
||||||
Self::Grid => !is_grid_edge(op_direction, idx, count),
|
Self::Grid => !is_grid_edge(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => idx != count - 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,10 +175,11 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::BSP => {
|
Self::BSP => {
|
||||||
if idx % 2 == 0 {
|
if idx.is_multiple_of(2) {
|
||||||
idx - 1
|
idx - 1
|
||||||
} else {
|
} else {
|
||||||
idx - 2
|
idx - 2
|
||||||
@@ -157,7 +191,8 @@ impl Direction for DefaultLayout {
|
|||||||
| Self::UltrawideVerticalStack
|
| Self::UltrawideVerticalStack
|
||||||
| Self::RightMainVerticalStack => idx - 1,
|
| Self::RightMainVerticalStack => idx - 1,
|
||||||
Self::HorizontalStack => 0,
|
Self::HorizontalStack => 0,
|
||||||
Self::Grid => grid_neighbor(op_direction, idx, count),
|
Self::Grid => grid_neighbor(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +201,7 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::BSP
|
Self::BSP
|
||||||
@@ -175,7 +211,8 @@ impl Direction for DefaultLayout {
|
|||||||
| Self::RightMainVerticalStack => idx + 1,
|
| Self::RightMainVerticalStack => idx + 1,
|
||||||
Self::Columns => unreachable!(),
|
Self::Columns => unreachable!(),
|
||||||
Self::HorizontalStack => 1,
|
Self::HorizontalStack => 1,
|
||||||
Self::Grid => grid_neighbor(op_direction, idx, count),
|
Self::Grid => grid_neighbor(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,10 +221,11 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::BSP => {
|
Self::BSP => {
|
||||||
if idx % 2 == 0 {
|
if idx.is_multiple_of(2) {
|
||||||
idx - 2
|
idx - 2
|
||||||
} else {
|
} else {
|
||||||
idx - 1
|
idx - 1
|
||||||
@@ -202,7 +240,8 @@ impl Direction for DefaultLayout {
|
|||||||
1 => unreachable!(),
|
1 => unreachable!(),
|
||||||
_ => 0,
|
_ => 0,
|
||||||
},
|
},
|
||||||
Self::Grid => grid_neighbor(op_direction, idx, count),
|
Self::Grid => grid_neighbor(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => idx - 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +250,7 @@ impl Direction for DefaultLayout {
|
|||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Self::BSP | Self::Columns | Self::HorizontalStack => idx + 1,
|
Self::BSP | Self::Columns | Self::HorizontalStack => idx + 1,
|
||||||
@@ -222,7 +262,8 @@ impl Direction for DefaultLayout {
|
|||||||
0 => 2,
|
0 => 2,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
Self::Grid => grid_neighbor(op_direction, idx, count),
|
Self::Grid => grid_neighbor(op_direction, idx, count, layout_options),
|
||||||
|
Self::Scrolling => idx + 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,21 +293,32 @@ struct GridTouchingEdges {
|
|||||||
clippy::cast_precision_loss,
|
clippy::cast_precision_loss,
|
||||||
clippy::cast_sign_loss
|
clippy::cast_sign_loss
|
||||||
)]
|
)]
|
||||||
fn get_grid_item(idx: usize, count: usize) -> GridItem {
|
fn get_grid_item(idx: usize, count: usize, layout_options: Option<LayoutOptions>) -> GridItem {
|
||||||
let num_cols = (count as f32).sqrt().ceil() as usize;
|
let row_constraint = layout_options.and_then(|o| o.grid.map(|g| g.rows));
|
||||||
|
let num_cols = if let Some(rows) = row_constraint {
|
||||||
|
((count as f32) / (rows as f32)).ceil() as i32
|
||||||
|
} else {
|
||||||
|
(count as f32).sqrt().ceil() as i32
|
||||||
|
};
|
||||||
|
|
||||||
let mut iter = 0;
|
let mut iter = 0;
|
||||||
|
|
||||||
for col in 0..num_cols {
|
for col in 0..num_cols {
|
||||||
let remaining_windows = count - iter;
|
let remaining_windows = (count - iter) as i32;
|
||||||
let remaining_columns = num_cols - col;
|
let remaining_columns = num_cols - col;
|
||||||
let num_rows_in_this_col = remaining_windows / remaining_columns;
|
|
||||||
|
let num_rows_in_this_col = if let Some(rows) = row_constraint {
|
||||||
|
(remaining_windows / remaining_columns).min(rows as i32)
|
||||||
|
} else {
|
||||||
|
remaining_windows / remaining_columns
|
||||||
|
};
|
||||||
|
|
||||||
for row in 0..num_rows_in_this_col {
|
for row in 0..num_rows_in_this_col {
|
||||||
if iter == idx {
|
if iter == idx {
|
||||||
return GridItem {
|
return GridItem {
|
||||||
state: GridItemState::Valid,
|
state: GridItemState::Valid,
|
||||||
row: row + 1,
|
row: (row + 1) as usize,
|
||||||
num_rows: num_rows_in_this_col,
|
num_rows: num_rows_in_this_col as usize,
|
||||||
touching_edges: GridTouchingEdges {
|
touching_edges: GridTouchingEdges {
|
||||||
left: col == 0,
|
left: col == 0,
|
||||||
right: col == num_cols - 1,
|
right: col == num_cols - 1,
|
||||||
@@ -293,8 +345,13 @@ fn get_grid_item(idx: usize, count: usize) -> GridItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_grid_edge(op_direction: OperationDirection, idx: usize, count: usize) -> bool {
|
fn is_grid_edge(
|
||||||
let item = get_grid_item(idx, count);
|
op_direction: OperationDirection,
|
||||||
|
idx: usize,
|
||||||
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
|
) -> bool {
|
||||||
|
let item = get_grid_item(idx, count, layout_options);
|
||||||
|
|
||||||
match item.state {
|
match item.state {
|
||||||
GridItemState::Invalid => false,
|
GridItemState::Invalid => false,
|
||||||
@@ -311,6 +368,7 @@ fn grid_neighbor(
|
|||||||
op_direction: Option<OperationDirection>,
|
op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: Option<usize>,
|
count: Option<usize>,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let Some(op_direction) = op_direction else {
|
let Some(op_direction) = op_direction else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -320,11 +378,11 @@ fn grid_neighbor(
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
let item = get_grid_item(idx, count);
|
let item = get_grid_item(idx, count, layout_options);
|
||||||
|
|
||||||
match op_direction {
|
match op_direction {
|
||||||
OperationDirection::Left => {
|
OperationDirection::Left => {
|
||||||
let item_from_prev_col = get_grid_item(idx - item.row, count);
|
let item_from_prev_col = get_grid_item(idx - item.row, count, layout_options);
|
||||||
|
|
||||||
if item.touching_edges.up && item.num_rows != item_from_prev_col.num_rows {
|
if item.touching_edges.up && item.num_rows != item_from_prev_col.num_rows {
|
||||||
return idx - (item.num_rows - 1);
|
return idx - (item.num_rows - 1);
|
||||||
@@ -348,36 +406,42 @@ impl Direction for CustomLayout {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
if count <= self.len() {
|
if count <= self.len() {
|
||||||
return DefaultLayout::Columns.index_in_direction(op_direction, idx, count);
|
return DefaultLayout::Columns.index_in_direction(
|
||||||
|
op_direction,
|
||||||
|
idx,
|
||||||
|
count,
|
||||||
|
layout_options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match op_direction {
|
match op_direction {
|
||||||
OperationDirection::Left => {
|
OperationDirection::Left => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.left_index(None, idx, None))
|
Option::from(self.left_index(None, idx, None, layout_options))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Right => {
|
OperationDirection::Right => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.right_index(None, idx, None))
|
Option::from(self.right_index(None, idx, None, layout_options))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Up => {
|
OperationDirection::Up => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.up_index(None, idx, None))
|
Option::from(self.up_index(None, idx, None, layout_options))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OperationDirection::Down => {
|
OperationDirection::Down => {
|
||||||
if self.is_valid_direction(op_direction, idx, count) {
|
if self.is_valid_direction(op_direction, idx, count, layout_options) {
|
||||||
Option::from(self.down_index(None, idx, None))
|
Option::from(self.down_index(None, idx, None, layout_options))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -390,9 +454,15 @@ impl Direction for CustomLayout {
|
|||||||
op_direction: OperationDirection,
|
op_direction: OperationDirection,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
count: usize,
|
count: usize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if count <= self.len() {
|
if count <= self.len() {
|
||||||
return DefaultLayout::Columns.is_valid_direction(op_direction, idx, count);
|
return DefaultLayout::Columns.is_valid_direction(
|
||||||
|
op_direction,
|
||||||
|
idx,
|
||||||
|
count,
|
||||||
|
layout_options,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
match op_direction {
|
match op_direction {
|
||||||
@@ -436,6 +506,7 @@ impl Direction for CustomLayout {
|
|||||||
_op_direction: Option<OperationDirection>,
|
_op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
_count: Option<usize>,
|
_count: Option<usize>,
|
||||||
|
_layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
idx - 1
|
idx - 1
|
||||||
}
|
}
|
||||||
@@ -445,6 +516,7 @@ impl Direction for CustomLayout {
|
|||||||
_op_direction: Option<OperationDirection>,
|
_op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
_count: Option<usize>,
|
_count: Option<usize>,
|
||||||
|
_layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
idx + 1
|
idx + 1
|
||||||
}
|
}
|
||||||
@@ -454,6 +526,7 @@ impl Direction for CustomLayout {
|
|||||||
_op_direction: Option<OperationDirection>,
|
_op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
_count: Option<usize>,
|
_count: Option<usize>,
|
||||||
|
_layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let column_idx = self.column_for_container_idx(idx);
|
let column_idx = self.column_for_container_idx(idx);
|
||||||
if column_idx - 1 == 0 {
|
if column_idx - 1 == 0 {
|
||||||
@@ -468,6 +541,7 @@ impl Direction for CustomLayout {
|
|||||||
_op_direction: Option<OperationDirection>,
|
_op_direction: Option<OperationDirection>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
_count: Option<usize>,
|
_count: Option<usize>,
|
||||||
|
_layout_options: Option<LayoutOptions>,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
let column_idx = self.column_for_container_idx(idx);
|
let column_idx = self.column_for_container_idx(idx);
|
||||||
self.first_container_idx(column_idx + 1)
|
self.first_container_idx(column_idx + 1)
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
#![allow(clippy::missing_errors_doc, clippy::use_self, clippy::doc_markdown)]
|
#![allow(clippy::missing_errors_doc, clippy::use_self, clippy::doc_markdown)]
|
||||||
|
|
||||||
|
use std::num::NonZeroUsize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
use crate::animation::prefix::AnimationPrefix;
|
|
||||||
use crate::KomorebiTheme;
|
use crate::KomorebiTheme;
|
||||||
|
use crate::animation::prefix::AnimationPrefix;
|
||||||
pub use animation::AnimationStyle;
|
pub use animation::AnimationStyle;
|
||||||
pub use arrangement::Arrangement;
|
pub use arrangement::Arrangement;
|
||||||
pub use arrangement::Axis;
|
pub use arrangement::Axis;
|
||||||
@@ -26,10 +27,10 @@ pub use default_layout::DefaultLayout;
|
|||||||
pub use direction::Direction;
|
pub use direction::Direction;
|
||||||
pub use layout::Layout;
|
pub use layout::Layout;
|
||||||
pub use operation_direction::OperationDirection;
|
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::PathExt;
|
||||||
pub use pathext::ResolvedPathBuf;
|
pub use pathext::ResolvedPathBuf;
|
||||||
|
pub use pathext::replace_env_in_path;
|
||||||
|
pub use pathext::resolve_option_hashmap_usize_path;
|
||||||
pub use rect::Rect;
|
pub use rect::Rect;
|
||||||
|
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
@@ -108,6 +109,7 @@ pub enum SocketMessage {
|
|||||||
AdjustWorkspacePadding(Sizing, i32),
|
AdjustWorkspacePadding(Sizing, i32),
|
||||||
ChangeLayout(DefaultLayout),
|
ChangeLayout(DefaultLayout),
|
||||||
CycleLayout(CycleDirection),
|
CycleLayout(CycleDirection),
|
||||||
|
ScrollingLayoutColumns(NonZeroUsize),
|
||||||
ChangeLayoutCustom(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
ChangeLayoutCustom(#[serde_as(as = "ResolvedPathBuf")] PathBuf),
|
||||||
FlipLayout(Axis),
|
FlipLayout(Axis),
|
||||||
ToggleWorkspaceWindowContainerBehaviour,
|
ToggleWorkspaceWindowContainerBehaviour,
|
||||||
@@ -200,6 +202,7 @@ pub enum SocketMessage {
|
|||||||
StackbarFontFamily(Option<String>),
|
StackbarFontFamily(Option<String>),
|
||||||
WorkAreaOffset(Rect),
|
WorkAreaOffset(Rect),
|
||||||
MonitorWorkAreaOffset(usize, Rect),
|
MonitorWorkAreaOffset(usize, Rect),
|
||||||
|
WorkspaceWorkAreaOffset(usize, usize, Rect),
|
||||||
ToggleWindowBasedWorkAreaOffset,
|
ToggleWindowBasedWorkAreaOffset,
|
||||||
ResizeDelta(i32),
|
ResizeDelta(i32),
|
||||||
InitialWorkspaceRule(ApplicationIdentifier, String, usize, usize),
|
InitialWorkspaceRule(ApplicationIdentifier, String, usize, usize),
|
||||||
@@ -245,7 +248,7 @@ pub enum SocketMessage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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())
|
Ok(serde_json::to_string(self)?.as_bytes().to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,7 +256,7 @@ impl SocketMessage {
|
|||||||
impl FromStr for SocketMessage {
|
impl FromStr for SocketMessage {
|
||||||
type Err = serde_json::Error;
|
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)
|
serde_json::from_str(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -547,7 +550,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn deserializes() {
|
fn deserializes() {
|
||||||
// Set a variable for testing
|
// Set a variable for testing
|
||||||
|
unsafe {
|
||||||
std::env::set_var("VAR", "VALUE");
|
std::env::set_var("VAR", "VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
let json = r#"{"type":"WorkspaceLayoutCustomRule","content":[0,0,0,"/path/%VAR%/d"]}"#;
|
let json = r#"{"type":"WorkspaceLayoutCustomRule","content":[0,0,0,"/path/%VAR%/d"]}"#;
|
||||||
let message: SocketMessage = serde_json::from_str(json).unwrap();
|
let message: SocketMessage = serde_json::from_str(json).unwrap();
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
|
use super::Axis;
|
||||||
|
use super::direction::Direction;
|
||||||
|
use crate::default_layout::LayoutOptions;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
use super::direction::Direction;
|
|
||||||
use super::Axis;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum)]
|
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub enum OperationDirection {
|
pub enum OperationDirection {
|
||||||
@@ -57,7 +57,8 @@ impl OperationDirection {
|
|||||||
layout_flip: Option<Axis>,
|
layout_flip: Option<Axis>,
|
||||||
idx: usize,
|
idx: usize,
|
||||||
len: NonZeroUsize,
|
len: NonZeroUsize,
|
||||||
|
layout_options: Option<LayoutOptions>,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
layout.index_in_direction(self.flip(layout_flip), idx, len.get())
|
layout.index_in_direction(self.flip(layout_flip), idx, len.get(), layout_options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::Component;
|
use std::path::Component;
|
||||||
use std::path::Path;
|
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 component is a variable, get the value from the environment
|
||||||
if let Some(var) = var {
|
if let Some(var) = var {
|
||||||
let var = unsafe { OsStr::from_encoded_bytes_unchecked(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);
|
out.push(value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -127,8 +126,8 @@ impl serde_with::schemars_0_8::JsonSchemaAs<PathBuf> for ResolvedPathBuf {
|
|||||||
"PathBuf".to_owned()
|
"PathBuf".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
|
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::schema::Schema {
|
||||||
<PathBuf as schemars::JsonSchema>::json_schema(gen)
|
<PathBuf as schemars::JsonSchema>::json_schema(generator)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +164,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn resolves_env_vars() {
|
fn resolves_env_vars() {
|
||||||
// Set a variable for testing
|
// Set a variable for testing
|
||||||
|
unsafe {
|
||||||
std::env::set_var("VAR", "VALUE");
|
std::env::set_var("VAR", "VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
// %VAR% format
|
// %VAR% format
|
||||||
assert_eq!(resolve("/path/%VAR%/d"), expected("/path/VALUE/d"));
|
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"));
|
assert_eq!(resolve("/path/$ASD/to/d"), expected("/path/$ASD/to/d"));
|
||||||
|
|
||||||
// Set a $env:USERPROFILE variable for testing
|
// Set a $env:USERPROFILE variable for testing
|
||||||
|
unsafe {
|
||||||
std::env::set_var("USERPROFILE", "C:\\Users\\user");
|
std::env::set_var("USERPROFILE", "C:\\Users\\user");
|
||||||
|
}
|
||||||
|
|
||||||
// ~ and $HOME should be replaced with $Env:USERPROFILE
|
// ~ and $HOME should be replaced with $Env:USERPROFILE
|
||||||
assert_eq!(resolve("~"), expected("C:\\Users\\user"));
|
assert_eq!(resolve("~"), expected("C:\\Users\\user"));
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ impl Rect {
|
|||||||
pub fn is_same_size_as(&self, rhs: &Self) -> bool {
|
pub fn is_same_size_as(&self, rhs: &Self) -> bool {
|
||||||
self.right == rhs.right && self.bottom == rhs.bottom
|
self.right == rhs.right && self.bottom == rhs.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_same_position_as(&self, rhs: &Self) -> bool {
|
||||||
|
self.left == rhs.left && self.top == rhs.top
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rect {
|
impl Rect {
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ pub fn send_notification(hwnd: isize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone()) {
|
match handle_notifications(wm.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -53,6 +54,7 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
|||||||
tracing::warn!("restarting failed thread: {}", error);
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-13
@@ -8,7 +8,7 @@ pub mod ring;
|
|||||||
pub mod container;
|
pub mod container;
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod focus_manager;
|
pub mod focus_manager;
|
||||||
pub mod locked_deque;
|
pub mod lockable_sequence;
|
||||||
pub mod monitor;
|
pub mod monitor;
|
||||||
pub mod monitor_reconciliator;
|
pub mod monitor_reconciliator;
|
||||||
pub mod process_command;
|
pub mod process_command;
|
||||||
@@ -16,7 +16,9 @@ pub mod process_event;
|
|||||||
pub mod process_movement;
|
pub mod process_movement;
|
||||||
pub mod reaper;
|
pub mod reaper;
|
||||||
pub mod set_window_position;
|
pub mod set_window_position;
|
||||||
|
pub mod splash;
|
||||||
pub mod stackbar_manager;
|
pub mod stackbar_manager;
|
||||||
|
pub mod state;
|
||||||
pub mod static_config;
|
pub mod static_config;
|
||||||
pub mod styles;
|
pub mod styles;
|
||||||
pub mod theme_manager;
|
pub mod theme_manager;
|
||||||
@@ -39,12 +41,12 @@ use std::io::Write;
|
|||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
use std::sync::atomic::AtomicI32;
|
use std::sync::atomic::AtomicI32;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub use core::*;
|
pub use core::*;
|
||||||
pub use komorebi_themes::colour::*;
|
pub use komorebi_themes::colour::*;
|
||||||
@@ -62,7 +64,7 @@ use crate::core::config_generation::IdWithIdentifier;
|
|||||||
use crate::core::config_generation::MatchingRule;
|
use crate::core::config_generation::MatchingRule;
|
||||||
use crate::core::config_generation::MatchingStrategy;
|
use crate::core::config_generation::MatchingStrategy;
|
||||||
use crate::core::config_generation::WorkspaceMatchingRule;
|
use crate::core::config_generation::WorkspaceMatchingRule;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre;
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
use os_info::Version;
|
use os_info::Version;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -70,10 +72,11 @@ use parking_lot::RwLock;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use state::State;
|
||||||
use uds_windows::UnixStream;
|
use uds_windows::UnixStream;
|
||||||
use which::which;
|
use which::which;
|
||||||
use winreg::enums::HKEY_CURRENT_USER;
|
|
||||||
use winreg::RegKey;
|
use winreg::RegKey;
|
||||||
|
use winreg::enums::HKEY_CURRENT_USER;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
|
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
|
||||||
@@ -200,8 +203,7 @@ lazy_static! {
|
|||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
home.is_dir(),
|
home.is_dir(),
|
||||||
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
|
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory"
|
||||||
home_path
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -212,11 +214,10 @@ lazy_static! {
|
|||||||
pub static ref AHK_EXE: String = {
|
pub static ref AHK_EXE: String = {
|
||||||
let mut ahk: String = String::from("autohotkey.exe");
|
let mut ahk: String = String::from("autohotkey.exe");
|
||||||
|
|
||||||
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE") {
|
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE")
|
||||||
if which(&komorebi_ahk_exe).is_ok() {
|
&& which(&komorebi_ahk_exe).is_ok() {
|
||||||
ahk = komorebi_ahk_exe;
|
ahk = komorebi_ahk_exe;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ahk
|
ahk
|
||||||
};
|
};
|
||||||
@@ -255,6 +256,29 @@ pub static WINDOW_HANDLING_BEHAVIOUR: AtomicCell<WindowHandlingBehaviour> =
|
|||||||
|
|
||||||
shadow_rs::shadow!(build);
|
shadow_rs::shadow!(build);
|
||||||
|
|
||||||
|
pub const PUBLIC_KEY: [u8; 32] = [
|
||||||
|
0x5a, 0x69, 0x4a, 0xe1, 0x3c, 0x4b, 0xc8, 0x4e, 0xc3, 0x79, 0x0f, 0xab, 0x27, 0x6b, 0x7e, 0xdd,
|
||||||
|
0x6b, 0x39, 0x6f, 0xa2, 0xc3, 0x9f, 0x3d, 0x48, 0xf2, 0x72, 0x56, 0x41, 0x1b, 0xc8, 0x08, 0xdb,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq, Deserialize)]
|
||||||
|
pub struct License {
|
||||||
|
#[serde(rename = "hasValidSubscription")]
|
||||||
|
pub has_valid_subscription: bool,
|
||||||
|
pub timestamp: i64,
|
||||||
|
#[serde(rename = "currentEndPeriod")]
|
||||||
|
pub current_end_period: Option<i64>,
|
||||||
|
pub signature: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for types that can be marked as locked or unlocked.
|
||||||
|
pub trait Lockable {
|
||||||
|
/// Returns `true` if the item is locked.
|
||||||
|
fn locked(&self) -> bool;
|
||||||
|
/// Sets the locked state of the item.
|
||||||
|
fn set_locked(&mut self, locked: bool) -> &mut Self;
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
||||||
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||||
@@ -299,7 +323,7 @@ pub fn current_virtual_desktop() -> Option<Vec<u8>> {
|
|||||||
current
|
current
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum NotificationEvent {
|
pub enum NotificationEvent {
|
||||||
@@ -316,14 +340,17 @@ pub enum VirtualDesktopNotification {
|
|||||||
LeftAssociatedVirtualDesktop,
|
LeftAssociatedVirtualDesktop,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Notification {
|
pub struct Notification {
|
||||||
pub event: NotificationEvent,
|
pub event: NotificationEvent,
|
||||||
pub state: State,
|
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!(
|
let is_override_event = matches!(
|
||||||
notification.event,
|
notification.event,
|
||||||
NotificationEvent::Socket(SocketMessage::AddSubscriberSocket(_))
|
NotificationEvent::Socket(SocketMessage::AddSubscriberSocket(_))
|
||||||
@@ -404,7 +431,7 @@ pub fn notify_subscribers(notification: Notification, state_has_been_modified: b
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_configuration() -> Result<()> {
|
pub fn load_configuration() -> eyre::Result<()> {
|
||||||
let config_pwsh = HOME_DIR.join("komorebi.ps1");
|
let config_pwsh = HOME_DIR.join("komorebi.ps1");
|
||||||
let config_ahk = HOME_DIR.join("komorebi.ahk");
|
let config_ahk = HOME_DIR.join("komorebi.ahk");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,357 @@
|
|||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use crate::Lockable;
|
||||||
|
|
||||||
|
/// A sequence supporting insertion, removal, and swapping of elements while preserving the absolute
|
||||||
|
/// positions of locked items.
|
||||||
|
pub trait LockableSequence<T: Lockable> {
|
||||||
|
/// Inserts a value at `idx`, keeping locked elements at their absolute positions.
|
||||||
|
fn insert_respecting_locks(&mut self, idx: usize, value: T) -> usize;
|
||||||
|
/// Removes the element at `idx`, keeping locked elements at their absolute positions.
|
||||||
|
fn remove_respecting_locks(&mut self, idx: usize) -> Option<T>;
|
||||||
|
/// Swaps the elements at indices `i` and `j`, keeping locked elements at their absolute positions.
|
||||||
|
fn swap_respecting_locks(&mut self, i: usize, j: usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Lockable> LockableSequence<T> for VecDeque<T> {
|
||||||
|
/// Insert `value` at logical index `idx`, with trying to keep locked elements
|
||||||
|
/// (`is_locked()`) anchored at their original positions.
|
||||||
|
///
|
||||||
|
/// Returns the final index of the inserted element.
|
||||||
|
fn insert_respecting_locks(&mut self, mut idx: usize, value: T) -> usize {
|
||||||
|
// 1. Bounds check: if index is out of range, simply append.
|
||||||
|
if idx >= self.len() {
|
||||||
|
self.push_back(value);
|
||||||
|
return self.len() - 1; // last index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Normal VecDeque insertion
|
||||||
|
self.insert(idx, value);
|
||||||
|
|
||||||
|
// 3. Walk left-to-right once, swapping any misplaced locked element. After
|
||||||
|
// the VecDeque::insert all items after `idx` have moved right by one. For every locked
|
||||||
|
// element that is now to the right of an unlocked one, swap it back left exactly once.
|
||||||
|
for index in (idx + 1)..self.len() {
|
||||||
|
if self[index].locked() && !self[index - 1].locked() {
|
||||||
|
self.swap(index - 1, index);
|
||||||
|
|
||||||
|
// If the element we just inserted participated in the swap,
|
||||||
|
// update `idx` so we can return its final location.
|
||||||
|
if idx == index - 1 {
|
||||||
|
idx = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove element at `idx`, with trying to keep locked elements
|
||||||
|
/// (`is_locked()`) anchored at their original positions.
|
||||||
|
///
|
||||||
|
/// Returns the removed element, or `None` if `idx` is out of bounds.
|
||||||
|
fn remove_respecting_locks(&mut self, idx: usize) -> Option<T> {
|
||||||
|
// 1. Bounds check: if index is out of range, do nothing.
|
||||||
|
if idx >= self.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Remove the element at the requested index.
|
||||||
|
// All elements after idx are now shifted left by 1.
|
||||||
|
let removed = self.remove(idx)?;
|
||||||
|
|
||||||
|
// 3. If less than 2 elements remain, nothing to shift.
|
||||||
|
if self.len() < 2 {
|
||||||
|
return Some(removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Iterate from the element just after the removed spot up to the second-to-last
|
||||||
|
// element, right-to-left. This loop "fixes" locked elements that were shifted left
|
||||||
|
// off their anchored positions: If a locked element now has an unlocked element
|
||||||
|
// to its right, swap them back to restore locked order.
|
||||||
|
for index in (idx..self.len() - 1).rev() {
|
||||||
|
// If current is locked and the next one is not locked, swap them.
|
||||||
|
if self[index].locked() && !self[index + 1].locked() {
|
||||||
|
self.swap(index, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Return the removed value.
|
||||||
|
Some(removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swaps the elements at indices `i` and `j`, along with their `locked` status, ensuring
|
||||||
|
/// the lock state remains associated with the position rather than the element itself.
|
||||||
|
fn swap_respecting_locks(&mut self, i: usize, j: usize) {
|
||||||
|
self.swap(i, j);
|
||||||
|
let locked_i = self[i].locked();
|
||||||
|
let locked_j = self[j].locked();
|
||||||
|
self[i].set_locked(locked_j);
|
||||||
|
self[j].set_locked(locked_i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
struct TestItem {
|
||||||
|
val: i32,
|
||||||
|
locked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lockable for TestItem {
|
||||||
|
fn locked(&self) -> bool {
|
||||||
|
self.locked
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_locked(&mut self, locked: bool) -> &mut Self {
|
||||||
|
self.locked = locked;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vals(v: &VecDeque<TestItem>) -> Vec<i32> {
|
||||||
|
v.iter().map(|x| x.val).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_deque(items: &[(i32, bool)]) -> VecDeque<TestItem> {
|
||||||
|
items
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|(val, locked)| TestItem { val, locked })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_respecting_locks() {
|
||||||
|
// Test case 1: Basic insertion with locked index
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
// Insert at index 0, should shift elements while keeping index 2 locked
|
||||||
|
ring.insert_respecting_locks(
|
||||||
|
0,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Element '2' remains at index 2, element '1' that was at index 1 is now at index 3
|
||||||
|
assert_eq!(vals(&ring), vec![99, 0, 2, 1, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 2: Insert at a locked index (should insert after locked)
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
// Try to insert at locked index 2, should insert at index 3 instead
|
||||||
|
let actual_index = ring.insert_respecting_locks(
|
||||||
|
2,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(actual_index, 3);
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 99, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 3: Multiple locked indices
|
||||||
|
{
|
||||||
|
// Lock index 1 and 3
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, true), (4, false)]);
|
||||||
|
// Insert at index 0, should maintain locked indices
|
||||||
|
ring.insert_respecting_locks(
|
||||||
|
0,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Elements '1' and '3' remain at indices 1 and 3
|
||||||
|
assert_eq!(vals(&ring), vec![99, 1, 0, 3, 2, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 4: Insert at end
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
let actual_index = ring.insert_respecting_locks(
|
||||||
|
5,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(actual_index, 5);
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4, 99]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 5: Empty ring
|
||||||
|
{
|
||||||
|
let mut ring = test_deque(&[]);
|
||||||
|
// Insert into empty deque
|
||||||
|
let actual_index = ring.insert_respecting_locks(
|
||||||
|
0,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(actual_index, 0);
|
||||||
|
assert_eq!(vals(&ring), vec![99]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 6: All indices locked
|
||||||
|
{
|
||||||
|
// Lock all indices
|
||||||
|
let mut ring = test_deque(&[(0, true), (1, true), (2, true), (3, true), (4, true)]);
|
||||||
|
// Try to insert at index 2, should insert at the end
|
||||||
|
let actual_index = ring.insert_respecting_locks(
|
||||||
|
2,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(actual_index, 5);
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4, 99]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 7: Consecutive locked indices
|
||||||
|
{
|
||||||
|
// Lock index 2 and 3
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, true), (4, false)]);
|
||||||
|
// Insert at index 1, should maintain consecutive locked indices
|
||||||
|
ring.insert_respecting_locks(
|
||||||
|
1,
|
||||||
|
TestItem {
|
||||||
|
val: 99,
|
||||||
|
locked: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// Elements '2' and '3' remain at indices 2 and 3
|
||||||
|
assert_eq!(vals(&ring), vec![0, 99, 2, 3, 1, 4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_remove_respecting_locks() {
|
||||||
|
// Test case 1: Remove a non-locked index before a locked index
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(0);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(0));
|
||||||
|
// Elements '2' remain at index 2
|
||||||
|
assert_eq!(vals(&ring), vec![1, 3, 2, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 2: Remove a locked index
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(2);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(2));
|
||||||
|
// Elements should stay at the same places
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 3: Remove an index after a locked index
|
||||||
|
{
|
||||||
|
// Lock index 1
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, false), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(3);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(3));
|
||||||
|
// Elements should stay at the same places
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 4: Multiple locked indices
|
||||||
|
{
|
||||||
|
// Lock index 1 and 3
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, true), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(0);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(0));
|
||||||
|
// Elements '1' and '3' remain at indices '1' and '3'
|
||||||
|
assert_eq!(vals(&ring), vec![2, 1, 4, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 5: Remove the last element
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(4);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(4));
|
||||||
|
// Index 2 should still be at the same place
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 6: Invalid index
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true), (3, false), (4, false)]);
|
||||||
|
let removed = ring.remove_respecting_locks(10);
|
||||||
|
assert_eq!(removed, None);
|
||||||
|
// Deque unchanged
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 3, 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 7: Remove enough elements to make a locked index invalid
|
||||||
|
{
|
||||||
|
// Lock index 2
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, false), (2, true)]);
|
||||||
|
ring.remove_respecting_locks(0);
|
||||||
|
// Index 2 should now be '1'
|
||||||
|
assert_eq!(vals(&ring), vec![1, 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test case 8: Removing an element before multiple locked indices
|
||||||
|
{
|
||||||
|
// Lock index 2 and 4
|
||||||
|
let mut ring = test_deque(&[
|
||||||
|
(0, false),
|
||||||
|
(1, false),
|
||||||
|
(2, true),
|
||||||
|
(3, false),
|
||||||
|
(4, true),
|
||||||
|
(5, false),
|
||||||
|
]);
|
||||||
|
let removed = ring.remove_respecting_locks(1);
|
||||||
|
assert_eq!(removed.map(|x| x.val), Some(1));
|
||||||
|
// Both indices should still be at the same place
|
||||||
|
assert_eq!(vals(&ring), vec![0, 3, 2, 5, 4]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_swap_respecting_locks_various_cases() {
|
||||||
|
// Swap unlocked and locked
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, true), (2, false), (3, false)]);
|
||||||
|
ring.swap_respecting_locks(0, 1);
|
||||||
|
assert_eq!(vals(&ring), vec![1, 0, 2, 3]);
|
||||||
|
assert!(!ring[0].locked);
|
||||||
|
assert!(ring[1].locked);
|
||||||
|
ring.swap_respecting_locks(0, 1);
|
||||||
|
assert_eq!(vals(&ring), vec![0, 1, 2, 3]);
|
||||||
|
assert!(!ring[0].locked);
|
||||||
|
assert!(ring[1].locked);
|
||||||
|
|
||||||
|
// Both locked
|
||||||
|
let mut ring = test_deque(&[(0, true), (1, false), (2, true)]);
|
||||||
|
ring.swap_respecting_locks(0, 2);
|
||||||
|
assert_eq!(vals(&ring), vec![2, 1, 0]);
|
||||||
|
assert!(ring[0].locked);
|
||||||
|
assert!(!ring[1].locked);
|
||||||
|
assert!(ring[2].locked);
|
||||||
|
|
||||||
|
// Both unlocked
|
||||||
|
let mut ring = test_deque(&[(0, false), (1, true), (2, false)]);
|
||||||
|
ring.swap_respecting_locks(0, 2);
|
||||||
|
assert_eq!(vals(&ring), vec![2, 1, 0]);
|
||||||
|
assert!(!ring[0].locked);
|
||||||
|
assert!(ring[1].locked);
|
||||||
|
assert!(!ring[2].locked);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
use std::collections::BTreeSet;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
pub struct LockedDeque<'a, T> {
|
|
||||||
deque: &'a mut VecDeque<T>,
|
|
||||||
locked_indices: &'a mut BTreeSet<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: PartialEq> LockedDeque<'a, T> {
|
|
||||||
pub fn new(deque: &'a mut VecDeque<T>, locked_indices: &'a mut BTreeSet<usize>) -> Self {
|
|
||||||
Self {
|
|
||||||
deque,
|
|
||||||
locked_indices,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, index: usize, value: T) -> usize {
|
|
||||||
insert_respecting_locks(self.deque, self.locked_indices, index, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove(&mut self, index: usize) -> Option<T> {
|
|
||||||
remove_respecting_locks(self.deque, self.locked_indices, index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_respecting_locks<T>(
|
|
||||||
deque: &mut VecDeque<T>,
|
|
||||||
locked_idx: &mut BTreeSet<usize>,
|
|
||||||
idx: usize,
|
|
||||||
value: T,
|
|
||||||
) -> usize {
|
|
||||||
if idx == deque.len() {
|
|
||||||
deque.push_back(value);
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_deque = VecDeque::with_capacity(deque.len() + 1);
|
|
||||||
let mut temp_locked_deque = VecDeque::new();
|
|
||||||
let mut j = 0;
|
|
||||||
let mut corrected_idx = idx;
|
|
||||||
|
|
||||||
for (i, el) in deque.drain(..).enumerate() {
|
|
||||||
if i == idx {
|
|
||||||
corrected_idx = j;
|
|
||||||
}
|
|
||||||
if locked_idx.contains(&i) {
|
|
||||||
temp_locked_deque.push_back(el);
|
|
||||||
} else {
|
|
||||||
new_deque.push_back(el);
|
|
||||||
j += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new_deque.insert(corrected_idx, value);
|
|
||||||
|
|
||||||
for (locked_el, locked_idx) in temp_locked_deque.into_iter().zip(locked_idx.iter()) {
|
|
||||||
new_deque.insert(*locked_idx, locked_el);
|
|
||||||
if *locked_idx <= corrected_idx {
|
|
||||||
corrected_idx += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*deque = new_deque;
|
|
||||||
|
|
||||||
corrected_idx
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_respecting_locks<T>(
|
|
||||||
deque: &mut VecDeque<T>,
|
|
||||||
locked_idx: &mut BTreeSet<usize>,
|
|
||||||
idx: usize,
|
|
||||||
) -> Option<T> {
|
|
||||||
if idx >= deque.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let final_size = deque.len() - 1;
|
|
||||||
|
|
||||||
let mut new_deque = VecDeque::with_capacity(final_size);
|
|
||||||
let mut temp_locked_deque = VecDeque::new();
|
|
||||||
let mut removed = None;
|
|
||||||
let mut removed_locked_idx = None;
|
|
||||||
|
|
||||||
for (i, el) in deque.drain(..).enumerate() {
|
|
||||||
if i == idx {
|
|
||||||
removed = Some(el);
|
|
||||||
removed_locked_idx = locked_idx.contains(&i).then_some(i);
|
|
||||||
} else if locked_idx.contains(&i) {
|
|
||||||
temp_locked_deque.push_back(el);
|
|
||||||
} else {
|
|
||||||
new_deque.push_back(el);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(i) = removed_locked_idx {
|
|
||||||
let mut above = locked_idx.split_off(&i);
|
|
||||||
above.pop_first();
|
|
||||||
locked_idx.extend(above.into_iter().map(|i| i - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
while locked_idx.last().is_some_and(|i| *i >= final_size) {
|
|
||||||
locked_idx.pop_last();
|
|
||||||
}
|
|
||||||
|
|
||||||
let extra_invalid_idx = (new_deque.len()
|
|
||||||
..(new_deque.len() + temp_locked_deque.len() - locked_idx.len()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for (locked_el, locked_idx) in temp_locked_deque
|
|
||||||
.into_iter()
|
|
||||||
.zip(locked_idx.iter().chain(extra_invalid_idx.iter()))
|
|
||||||
{
|
|
||||||
new_deque.insert(*locked_idx, locked_el);
|
|
||||||
}
|
|
||||||
|
|
||||||
*deque = new_deque;
|
|
||||||
|
|
||||||
removed
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_insert_respecting_locks() {
|
|
||||||
// Test case 1: Basic insertion with locked index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
// Insert at index 0, should shift elements while keeping index 2 locked
|
|
||||||
insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![99, 0, 2, 1, 3, 4]));
|
|
||||||
// Element '2' remains at index 2, element '1' that was at index 1 is now at index 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 2: Insert at a locked index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
// Try to insert at locked index 2, should insert at index 3 instead
|
|
||||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 2, 99);
|
|
||||||
assert_eq!(actual_index, 3);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 99, 3, 4]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 3: Multiple locked indices
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(1); // Lock index 1
|
|
||||||
locked.insert(3); // Lock index 3
|
|
||||||
|
|
||||||
// Insert at index 0, should maintain locked indices
|
|
||||||
insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![99, 1, 0, 3, 2, 4]));
|
|
||||||
// Elements '1' and '3' remain at indices 1 and 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 4: Insert at end
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
// Insert at end of deque
|
|
||||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 5, 99);
|
|
||||||
assert_eq!(actual_index, 5);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4, 99]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 5: Empty deque
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::new();
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
|
|
||||||
// Insert into empty deque
|
|
||||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 0, 99);
|
|
||||||
assert_eq!(actual_index, 0);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![99]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 6: All indices locked
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
for i in 0..5 {
|
|
||||||
locked.insert(i); // Lock all indices
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to insert at index 2, should insert at the end
|
|
||||||
let actual_index = insert_respecting_locks(&mut deque, &mut locked, 2, 99);
|
|
||||||
assert_eq!(actual_index, 5);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4, 99]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 7: Consecutive locked indices
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
locked.insert(3); // Lock index 3
|
|
||||||
|
|
||||||
// Insert at index 1, should maintain consecutive locked indices
|
|
||||||
insert_respecting_locks(&mut deque, &mut locked, 1, 99);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 99, 2, 3, 1, 4]));
|
|
||||||
// Elements '2' and '3' remain at indices 2 and 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_remove_respecting_locks() {
|
|
||||||
// Test case 1: Remove a non-locked index before a locked index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 0);
|
|
||||||
assert_eq!(removed, Some(0));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![1, 3, 2, 4]));
|
|
||||||
assert!(locked.contains(&2)); // Index 2 should still be locked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 2: Remove a locked index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 2);
|
|
||||||
assert_eq!(removed, Some(2));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 3, 4]));
|
|
||||||
assert!(!locked.contains(&2)); // Index 2 should be unlocked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 3: Remove an index after a locked index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(1); // Lock index 1
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 3);
|
|
||||||
assert_eq!(removed, Some(3));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 4]));
|
|
||||||
assert!(locked.contains(&1)); // Index 1 should still be locked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 4: Multiple locked indices
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(1); // Lock index 1
|
|
||||||
locked.insert(3); // Lock index 3
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 0);
|
|
||||||
assert_eq!(removed, Some(0));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![2, 1, 4, 3]));
|
|
||||||
assert!(locked.contains(&1) && locked.contains(&3)); // Both indices should still be locked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 5: Remove the last element
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 4);
|
|
||||||
assert_eq!(removed, Some(4));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3]));
|
|
||||||
assert!(locked.contains(&2)); // Index 2 should still be locked
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 6: Invalid index
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 10);
|
|
||||||
assert_eq!(removed, None);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 1, 2, 3, 4])); // Deque unchanged
|
|
||||||
assert!(locked.contains(&2)); // Lock unchanged
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 7: Remove enough elements to make a locked index invalid
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
|
|
||||||
remove_respecting_locks(&mut deque, &mut locked, 0);
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![1, 2]));
|
|
||||||
assert!(!locked.contains(&2)); // Index 2 should now be invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test case 8: Removing an element before multiple locked indices
|
|
||||||
{
|
|
||||||
let mut deque = VecDeque::from(vec![0, 1, 2, 3, 4, 5]);
|
|
||||||
let mut locked = BTreeSet::new();
|
|
||||||
locked.insert(2); // Lock index 2
|
|
||||||
locked.insert(4); // Lock index 4
|
|
||||||
|
|
||||||
let removed = remove_respecting_locks(&mut deque, &mut locked, 1);
|
|
||||||
assert_eq!(removed, Some(1));
|
|
||||||
assert_eq!(deque, VecDeque::from(vec![0, 3, 2, 5, 4]));
|
|
||||||
assert!(locked.contains(&2) && locked.contains(&4)); // Both indices should still be locked
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+28
-22
@@ -10,31 +10,36 @@
|
|||||||
use std::env::temp_dir;
|
use std::env::temp_dir;
|
||||||
use std::net::Shutdown;
|
use std::net::Shutdown;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre::bail;
|
||||||
use crossbeam_utils::Backoff;
|
use crossbeam_utils::Backoff;
|
||||||
use komorebi::animation::AnimationEngine;
|
|
||||||
use komorebi::animation::ANIMATION_ENABLED_GLOBAL;
|
use komorebi::animation::ANIMATION_ENABLED_GLOBAL;
|
||||||
use komorebi::animation::ANIMATION_ENABLED_PER_ANIMATION;
|
use komorebi::animation::ANIMATION_ENABLED_PER_ANIMATION;
|
||||||
|
use komorebi::animation::AnimationEngine;
|
||||||
use komorebi::replace_env_in_path;
|
use komorebi::replace_env_in_path;
|
||||||
|
use parking_lot::Mutex;
|
||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
use parking_lot::deadlock;
|
use parking_lot::deadlock;
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sysinfo::Process;
|
use sysinfo::Process;
|
||||||
use sysinfo::ProcessesToUpdate;
|
use sysinfo::ProcessesToUpdate;
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use uds_windows::UnixStream;
|
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::border_manager;
|
||||||
use komorebi::focus_manager;
|
use komorebi::focus_manager;
|
||||||
use komorebi::load_configuration;
|
use komorebi::load_configuration;
|
||||||
@@ -45,27 +50,25 @@ use komorebi::process_event::listen_for_events;
|
|||||||
use komorebi::process_movement::listen_for_movements;
|
use komorebi::process_movement::listen_for_movements;
|
||||||
use komorebi::reaper;
|
use komorebi::reaper;
|
||||||
use komorebi::stackbar_manager;
|
use komorebi::stackbar_manager;
|
||||||
|
use komorebi::state::State;
|
||||||
use komorebi::static_config::StaticConfig;
|
use komorebi::static_config::StaticConfig;
|
||||||
use komorebi::theme_manager;
|
use komorebi::theme_manager;
|
||||||
use komorebi::transparency_manager;
|
use komorebi::transparency_manager;
|
||||||
use komorebi::window_manager::State;
|
|
||||||
use komorebi::window_manager::WindowManager;
|
use komorebi::window_manager::WindowManager;
|
||||||
use komorebi::windows_api::WindowsApi;
|
use komorebi::windows_api::WindowsApi;
|
||||||
use komorebi::winevent_listener;
|
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() {
|
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||||
|
unsafe {
|
||||||
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
if std::env::var("RUST_LOG").is_err() {
|
||||||
|
unsafe {
|
||||||
std::env::set_var(
|
std::env::set_var(
|
||||||
"RUST_LOG",
|
"RUST_LOG",
|
||||||
match log_level {
|
match log_level {
|
||||||
@@ -77,6 +80,7 @@ fn setup(log_level: LogLevel) -> Result<(WorkerGuard, WorkerGuard)> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let appender = tracing_appender::rolling::daily(std::env::temp_dir(), "komorebi_plaintext.log");
|
let appender = tracing_appender::rolling::daily(std::env::temp_dir(), "komorebi_plaintext.log");
|
||||||
let color_appender = tracing_appender::rolling::daily(std::env::temp_dir(), "komorebi.log");
|
let color_appender = tracing_appender::rolling::daily(std::env::temp_dir(), "komorebi.log");
|
||||||
@@ -134,7 +138,8 @@ fn setup(log_level: LogLevel) -> Result<(WorkerGuard, WorkerGuard)> {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
fn detect_deadlocks() {
|
fn detect_deadlocks() {
|
||||||
// Create a background thread which checks for deadlocks every 10s
|
// Create a background thread which checks for deadlocks every 10s
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
tracing::info!("running deadlock detector");
|
tracing::info!("running deadlock detector");
|
||||||
std::thread::sleep(Duration::from_secs(5));
|
std::thread::sleep(Duration::from_secs(5));
|
||||||
let deadlocks = deadlock::check_deadlock();
|
let deadlocks = deadlock::check_deadlock();
|
||||||
@@ -150,6 +155,7 @@ fn detect_deadlocks() {
|
|||||||
tracing::error!("{:#?}", t.backtrace());
|
tracing::error!("{:#?}", t.backtrace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +196,7 @@ struct Opts {
|
|||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
fn main() -> Result<()> {
|
fn main() -> eyre::Result<()> {
|
||||||
let opts: Opts = Opts::parse();
|
let opts: Opts = Opts::parse();
|
||||||
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
|
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
|
||||||
|
|
||||||
@@ -210,9 +216,7 @@ fn main() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if set_foreground_window_retries == 0 {
|
if set_foreground_window_retries == 0 {
|
||||||
return Err(anyhow!(
|
bail!("failed call to AllowSetForegroundWindow after 5 retries");
|
||||||
"failed call to AllowSetForegroundWindow after 5 retries"
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,15 +233,17 @@ fn main() -> Result<()> {
|
|||||||
if matched_procs.len() > 1 {
|
if matched_procs.len() > 1 {
|
||||||
let mut len = matched_procs.len();
|
let mut len = matched_procs.len();
|
||||||
for proc in matched_procs {
|
for proc in matched_procs {
|
||||||
if let Some(executable_path) = proc.exe() {
|
if let Some(executable_path) = proc.exe()
|
||||||
if executable_path.to_string_lossy().contains("shims") {
|
&& executable_path.to_string_lossy().contains("shims")
|
||||||
|
{
|
||||||
len -= 1;
|
len -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if 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);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-72
@@ -2,13 +2,9 @@ use std::collections::HashMap;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::atomic::Ordering;
|
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::eyre::bail;
|
||||||
use color_eyre::Result;
|
|
||||||
use getset::CopyGetters;
|
|
||||||
use getset::Getters;
|
|
||||||
use getset::MutGetters;
|
|
||||||
use getset::Setters;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -17,58 +13,40 @@ use crate::border_manager::BORDER_OFFSET;
|
|||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
use crate::core::Rect;
|
use crate::core::Rect;
|
||||||
|
|
||||||
use crate::container::Container;
|
use crate::DEFAULT_CONTAINER_PADDING;
|
||||||
use crate::ring::Ring;
|
use crate::DEFAULT_WORKSPACE_PADDING;
|
||||||
use crate::workspace::Workspace;
|
|
||||||
use crate::workspace::WorkspaceGlobals;
|
|
||||||
use crate::workspace::WorkspaceLayer;
|
|
||||||
use crate::DefaultLayout;
|
use crate::DefaultLayout;
|
||||||
use crate::FloatingLayerBehaviour;
|
use crate::FloatingLayerBehaviour;
|
||||||
use crate::Layout;
|
use crate::Layout;
|
||||||
use crate::OperationDirection;
|
use crate::OperationDirection;
|
||||||
use crate::Wallpaper;
|
use crate::Wallpaper;
|
||||||
use crate::WindowsApi;
|
use crate::WindowsApi;
|
||||||
use crate::DEFAULT_CONTAINER_PADDING;
|
use crate::container::Container;
|
||||||
use crate::DEFAULT_WORKSPACE_PADDING;
|
use crate::ring::Ring;
|
||||||
|
use crate::workspace::Workspace;
|
||||||
|
use crate::workspace::WorkspaceGlobals;
|
||||||
|
use crate::workspace::WorkspaceLayer;
|
||||||
|
|
||||||
#[derive(
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
Debug, Clone, Serialize, Deserialize, Getters, CopyGetters, MutGetters, Setters, PartialEq,
|
|
||||||
)]
|
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub struct Monitor {
|
pub struct Monitor {
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub id: isize,
|
pub id: isize,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub device: String,
|
pub device: String,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub device_id: String,
|
pub device_id: String,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub serial_number_id: Option<String>,
|
pub serial_number_id: Option<String>,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub size: Rect,
|
pub size: Rect,
|
||||||
#[getset(get = "pub", set = "pub")]
|
|
||||||
pub work_area_size: Rect,
|
pub work_area_size: Rect,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub work_area_offset: Option<Rect>,
|
pub work_area_offset: Option<Rect>,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub window_based_work_area_offset: Option<Rect>,
|
pub window_based_work_area_offset: Option<Rect>,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub window_based_work_area_offset_limit: isize,
|
pub window_based_work_area_offset_limit: isize,
|
||||||
pub workspaces: Ring<Workspace>,
|
pub workspaces: Ring<Workspace>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub last_focused_workspace: Option<usize>,
|
pub last_focused_workspace: Option<usize>,
|
||||||
#[getset(get_mut = "pub")]
|
|
||||||
pub workspace_names: HashMap<usize, String>,
|
pub workspace_names: HashMap<usize, String>,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub container_padding: Option<i32>,
|
pub container_padding: Option<i32>,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub workspace_padding: Option<i32>,
|
pub workspace_padding: Option<i32>,
|
||||||
#[getset(get = "pub", get_mut = "pub", set = "pub")]
|
|
||||||
pub wallpaper: Option<Wallpaper>,
|
pub wallpaper: Option<Wallpaper>,
|
||||||
#[getset(get_copy = "pub", set = "pub")]
|
|
||||||
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
pub floating_layer_behaviour: Option<FloatingLayerBehaviour>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,23 +153,23 @@ impl Monitor {
|
|||||||
|
|
||||||
pub fn focused_workspace_name(&self) -> Option<String> {
|
pub fn focused_workspace_name(&self) -> Option<String> {
|
||||||
self.focused_workspace()
|
self.focused_workspace()
|
||||||
.map(|w| w.name().clone())
|
.map(|w| w.name.clone())
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn focused_workspace_layout(&self) -> Option<Layout> {
|
pub fn focused_workspace_layout(&self) -> Option<Layout> {
|
||||||
self.focused_workspace().and_then(|workspace| {
|
self.focused_workspace().and_then(|workspace| {
|
||||||
if *workspace.tile() {
|
if workspace.tile {
|
||||||
Some(workspace.layout().clone())
|
Some(workspace.layout.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
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 focused_idx = self.focused_workspace_idx();
|
||||||
let hmonitor = self.id();
|
let hmonitor = self.id;
|
||||||
let monitor_wp = self.wallpaper.clone();
|
let monitor_wp = self.wallpaper.clone();
|
||||||
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
|
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
|
||||||
if i == focused_idx {
|
if i == focused_idx {
|
||||||
@@ -207,10 +185,10 @@ impl Monitor {
|
|||||||
/// Updates the `globals` field of all workspaces
|
/// Updates the `globals` field of all workspaces
|
||||||
pub fn update_workspaces_globals(&mut self, offset: Option<Rect>) {
|
pub fn update_workspaces_globals(&mut self, offset: Option<Rect>) {
|
||||||
let container_padding = self
|
let container_padding = self
|
||||||
.container_padding()
|
.container_padding
|
||||||
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
|
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
|
||||||
let workspace_padding = self
|
let workspace_padding = self
|
||||||
.workspace_padding()
|
.workspace_padding
|
||||||
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
||||||
let (border_width, border_offset) = {
|
let (border_width, border_offset) = {
|
||||||
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
||||||
@@ -222,11 +200,11 @@ impl Monitor {
|
|||||||
(0, 0)
|
(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 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 = self.window_based_work_area_offset;
|
||||||
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
|
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit;
|
||||||
let floating_layer_behaviour = self.floating_layer_behaviour();
|
let floating_layer_behaviour = self.floating_layer_behaviour;
|
||||||
|
|
||||||
for workspace in self.workspaces_mut() {
|
for workspace in self.workspaces_mut() {
|
||||||
workspace.globals = WorkspaceGlobals {
|
workspace.globals = WorkspaceGlobals {
|
||||||
@@ -246,10 +224,10 @@ impl Monitor {
|
|||||||
/// Updates the `globals` field of workspace with index `workspace_idx`
|
/// Updates the `globals` field of workspace with index `workspace_idx`
|
||||||
pub fn update_workspace_globals(&mut self, workspace_idx: usize, offset: Option<Rect>) {
|
pub fn update_workspace_globals(&mut self, workspace_idx: usize, offset: Option<Rect>) {
|
||||||
let container_padding = self
|
let container_padding = self
|
||||||
.container_padding()
|
.container_padding
|
||||||
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
|
.or(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
|
||||||
let workspace_padding = self
|
let workspace_padding = self
|
||||||
.workspace_padding()
|
.workspace_padding
|
||||||
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
.or(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
||||||
let (border_width, border_offset) = {
|
let (border_width, border_offset) = {
|
||||||
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
let border_enabled = BORDER_ENABLED.load(Ordering::SeqCst);
|
||||||
@@ -261,11 +239,11 @@ impl Monitor {
|
|||||||
(0, 0)
|
(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 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 = self.window_based_work_area_offset;
|
||||||
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit();
|
let window_based_work_area_offset_limit = self.window_based_work_area_offset_limit;
|
||||||
let floating_layer_behaviour = self.floating_layer_behaviour();
|
let floating_layer_behaviour = self.floating_layer_behaviour;
|
||||||
|
|
||||||
if let Some(workspace) = self.workspaces_mut().get_mut(workspace_idx) {
|
if let Some(workspace) = self.workspaces_mut().get_mut(workspace_idx) {
|
||||||
workspace.globals = WorkspaceGlobals {
|
workspace.globals = WorkspaceGlobals {
|
||||||
@@ -286,14 +264,14 @@ impl Monitor {
|
|||||||
&mut self,
|
&mut self,
|
||||||
container: Container,
|
container: Container,
|
||||||
workspace_idx: Option<usize>,
|
workspace_idx: Option<usize>,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let workspace = if let Some(idx) = workspace_idx {
|
let workspace = if let Some(idx) = workspace_idx {
|
||||||
self.workspaces_mut()
|
self.workspaces_mut()
|
||||||
.get_mut(idx)
|
.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 {
|
} else {
|
||||||
self.focused_workspace_mut()
|
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);
|
workspace.add_container_to_back(container);
|
||||||
@@ -310,21 +288,21 @@ impl Monitor {
|
|||||||
container: Container,
|
container: Container,
|
||||||
workspace_idx: Option<usize>,
|
workspace_idx: Option<usize>,
|
||||||
direction: OperationDirection,
|
direction: OperationDirection,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let workspace = if let Some(idx) = workspace_idx {
|
let workspace = if let Some(idx) = workspace_idx {
|
||||||
self.workspaces_mut()
|
self.workspaces_mut()
|
||||||
.get_mut(idx)
|
.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 {
|
} else {
|
||||||
self.focused_workspace_mut()
|
self.focused_workspace_mut()
|
||||||
.ok_or_else(|| anyhow!("there is no workspace"))?
|
.ok_or_eyre("there is no workspace")?
|
||||||
};
|
};
|
||||||
|
|
||||||
match direction {
|
match direction {
|
||||||
OperationDirection::Left => {
|
OperationDirection::Left => {
|
||||||
// insert the container into the workspace on the monitor at the back (or rightmost position)
|
// 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)
|
// 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 {
|
Layout::Default(layout) => match layout {
|
||||||
DefaultLayout::RightMainVerticalStack => {
|
DefaultLayout::RightMainVerticalStack => {
|
||||||
workspace.add_container_to_front(container);
|
workspace.add_container_to_front(container);
|
||||||
@@ -348,7 +326,7 @@ impl Monitor {
|
|||||||
OperationDirection::Right => {
|
OperationDirection::Right => {
|
||||||
// insert the container into the workspace on the monitor at the front (or leftmost position)
|
// 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)
|
// 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) => {
|
Layout::Default(layout) => {
|
||||||
let target_index = layout.leftmost_index(workspace.containers().len());
|
let target_index = layout.leftmost_index(workspace.containers().len());
|
||||||
|
|
||||||
@@ -412,12 +390,12 @@ impl Monitor {
|
|||||||
target_workspace_idx: usize,
|
target_workspace_idx: usize,
|
||||||
follow: bool,
|
follow: bool,
|
||||||
direction: Option<OperationDirection>,
|
direction: Option<OperationDirection>,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let workspace = self
|
let workspace = self
|
||||||
.focused_workspace_mut()
|
.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");
|
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.floating_windows_mut().push_back(window);
|
||||||
target_workspace.set_layer(WorkspaceLayer::Floating);
|
target_workspace.layer = WorkspaceLayer::Floating;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let container = workspace
|
let container = workspace
|
||||||
.remove_focused_container()
|
.remove_focused_container()
|
||||||
.ok_or_else(|| anyhow!("there is no container"))?;
|
.ok_or_eyre("there is no container")?;
|
||||||
|
|
||||||
let workspaces = self.workspaces_mut();
|
let workspaces = self.workspaces_mut();
|
||||||
|
|
||||||
@@ -458,7 +436,7 @@ impl Monitor {
|
|||||||
Some(workspace) => workspace,
|
Some(workspace) => workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
if target_workspace.monocle_container().is_some() {
|
if target_workspace.monocle_container.is_some() {
|
||||||
for container in target_workspace.containers_mut() {
|
for container in target_workspace.containers_mut() {
|
||||||
container.restore();
|
container.restore();
|
||||||
}
|
}
|
||||||
@@ -470,7 +448,7 @@ impl Monitor {
|
|||||||
target_workspace.reintegrate_monocle_container()?;
|
target_workspace.reintegrate_monocle_container()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_workspace.set_layer(WorkspaceLayer::Tiling);
|
target_workspace.layer = WorkspaceLayer::Tiling;
|
||||||
|
|
||||||
if let Some(direction) = direction {
|
if let Some(direction) = direction {
|
||||||
self.add_container_with_direction(
|
self.add_container_with_direction(
|
||||||
@@ -491,7 +469,7 @@ impl Monitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(self))]
|
#[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");
|
tracing::info!("focusing workspace");
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -500,7 +478,7 @@ impl Monitor {
|
|||||||
if workspaces.get(idx).is_none() {
|
if workspaces.get(idx).is_none() {
|
||||||
workspaces.resize(idx + 1, Workspace::default());
|
workspaces.resize(idx + 1, Workspace::default());
|
||||||
}
|
}
|
||||||
|
self.last_focused_workspace = Some(self.workspaces.focused_idx());
|
||||||
self.workspaces.focus(idx);
|
self.workspaces.focus(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,8 +488,8 @@ impl Monitor {
|
|||||||
if name.is_some() {
|
if name.is_some() {
|
||||||
self.workspaces_mut()
|
self.workspaces_mut()
|
||||||
.get_mut(idx)
|
.get_mut(idx)
|
||||||
.ok_or_else(|| anyhow!("there is no workspace"))?
|
.ok_or_eyre("there is no workspace")?
|
||||||
.set_name(name);
|
.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,9 +500,9 @@ impl Monitor {
|
|||||||
self.workspaces().len()
|
self.workspaces().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_focused_workspace(&mut self, offset: Option<Rect>) -> Result<()> {
|
pub fn update_focused_workspace(&mut self, offset: Option<Rect>) -> eyre::Result<()> {
|
||||||
let offset = if self.work_area_offset().is_some() {
|
let offset = if self.work_area_offset.is_some() {
|
||||||
self.work_area_offset()
|
self.work_area_offset
|
||||||
} else {
|
} else {
|
||||||
offset
|
offset
|
||||||
};
|
};
|
||||||
@@ -532,7 +510,7 @@ impl Monitor {
|
|||||||
let focused_workspace_idx = self.focused_workspace_idx();
|
let focused_workspace_idx = self.focused_workspace_idx();
|
||||||
self.update_workspace_globals(focused_workspace_idx, offset);
|
self.update_workspace_globals(focused_workspace_idx, offset);
|
||||||
self.focused_workspace_mut()
|
self.focused_workspace_mut()
|
||||||
.ok_or_else(|| anyhow!("there is no workspace"))?
|
.ok_or_eyre("there is no workspace")?
|
||||||
.update()?;
|
.update()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use windows::core::PCWSTR;
|
|
||||||
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_DISPLAY_ADAPTER;
|
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_DISPLAY_ADAPTER;
|
||||||
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_MONITOR;
|
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_MONITOR;
|
||||||
use windows::Win32::Devices::Display::GUID_DEVINTERFACE_VIDEO_OUTPUT_ARRIVAL;
|
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::Foundation::WPARAM;
|
||||||
use windows::Win32::System::Power::POWERBROADCAST_SETTING;
|
use windows::Win32::System::Power::POWERBROADCAST_SETTING;
|
||||||
use windows::Win32::System::SystemServices::GUID_LIDSWITCH_STATE_CHANGE;
|
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_HREDRAW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DBT_CONFIGCHANGED;
|
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_DEVNODES_CHANGED;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVTYP_DEVICEINTERFACE;
|
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVTYP_DEVICEINTERFACE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DEV_BROADCAST_DEVICEINTERFACE_W;
|
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::MSG;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::PBT_APMRESUMEAUTOMATIC;
|
use windows::Win32::UI::WindowsAndMessaging::PBT_APMRESUMEAUTOMATIC;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::PBT_APMRESUMESUSPEND;
|
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::PBT_POWERSETTINGCHANGE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS;
|
use windows::Win32::UI::WindowsAndMessaging::REGISTER_NOTIFICATION_FLAGS;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SPI_SETWORKAREA;
|
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_DEVICECHANGE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE;
|
use windows::Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_POWERBROADCAST;
|
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::WNDCLASSW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_LOCK;
|
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_LOCK;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_UNLOCK;
|
use windows::Win32::UI::WindowsAndMessaging::WTS_SESSION_UNLOCK;
|
||||||
|
use windows::core::PCWSTR;
|
||||||
|
|
||||||
|
use crate::WindowsApi;
|
||||||
use crate::monitor_reconciliator;
|
use crate::monitor_reconciliator;
|
||||||
use crate::windows_api;
|
use crate::windows_api;
|
||||||
use crate::WindowsApi;
|
|
||||||
|
|
||||||
// This is a hidden window specifically spawned to listen to system-wide events related to monitors
|
// This is a hidden window specifically spawned to listen to system-wide events related to monitors
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
@@ -224,14 +224,18 @@ impl Hidden {
|
|||||||
WM_WTSSESSION_CHANGE => {
|
WM_WTSSESSION_CHANGE => {
|
||||||
match wparam.0 as u32 {
|
match wparam.0 as u32 {
|
||||||
WTS_SESSION_LOCK => {
|
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::send_notification(
|
||||||
monitor_reconciliator::MonitorNotification::SessionLocked,
|
monitor_reconciliator::MonitorNotification::SessionLocked,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
WTS_SESSION_UNLOCK => {
|
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::send_notification(
|
||||||
monitor_reconciliator::MonitorNotification::SessionUnlocked,
|
monitor_reconciliator::MonitorNotification::SessionUnlocked,
|
||||||
@@ -251,7 +255,8 @@ impl Hidden {
|
|||||||
// and resolution changes here
|
// and resolution changes here
|
||||||
WM_DISPLAYCHANGE => {
|
WM_DISPLAYCHANGE => {
|
||||||
tracing::debug!(
|
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(
|
monitor_reconciliator::send_notification(
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![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::WORKSPACE_MATCHING_RULES;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use crate::WindowsApi;
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
use crate::config_generation::WorkspaceMatchingRule;
|
use crate::config_generation::WorkspaceMatchingRule;
|
||||||
use crate::core::Rect;
|
use crate::core::Rect;
|
||||||
@@ -7,14 +14,7 @@ use crate::monitor;
|
|||||||
use crate::monitor::Monitor;
|
use crate::monitor::Monitor;
|
||||||
use crate::monitor_reconciliator::hidden::Hidden;
|
use crate::monitor_reconciliator::hidden::Hidden;
|
||||||
use crate::notify_subscribers;
|
use crate::notify_subscribers;
|
||||||
use crate::Notification;
|
use crate::state::State;
|
||||||
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::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
@@ -22,10 +22,10 @@ use parking_lot::Mutex;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
pub mod hidden;
|
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) {
|
pub fn insert_in_monitor_cache(serial_or_device_id: &str, monitor: Monitor) {
|
||||||
let dip = DISPLAY_INDEX_PREFERENCES.read();
|
let dip = DISPLAY_INDEX_PREFERENCES.read();
|
||||||
let mut dip_ids = dip.values();
|
let mut dip_ids = dip.values();
|
||||||
let preferred_id = if dip_ids.any(|id| id == monitor.device_id()) {
|
let preferred_id = if dip_ids.any(|id| id.eq(&monitor.device_id)) {
|
||||||
monitor.device_id().clone()
|
monitor.device_id.clone()
|
||||||
} else if dip_ids.any(|id| Some(id) == monitor.serial_number_id().as_ref()) {
|
} else if dip_ids.any(|id| Some(id) == monitor.serial_number_id.as_ref()) {
|
||||||
monitor.serial_number_id().clone().unwrap_or_default()
|
monitor.serial_number_id.clone().unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
serial_or_device_id.to_string()
|
serial_or_device_id.to_string()
|
||||||
};
|
};
|
||||||
@@ -100,15 +100,15 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
for d in &all_displays {
|
for d in &all_displays {
|
||||||
if let Some(id) = &d.serial_number_id {
|
if let Some(id) = &d.serial_number_id
|
||||||
if serial_id_map.get(id).copied().unwrap_or_default() > 1 {
|
&& serial_id_map.get(id).copied().unwrap_or_default() > 1
|
||||||
|
{
|
||||||
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
|
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
|
||||||
if !dupes.contains(id) {
|
if !dupes.contains(id) {
|
||||||
(*dupes).push(id.clone());
|
(*dupes).push(id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(all_displays
|
Ok(all_displays
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -155,7 +155,8 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Re
|
|||||||
|
|
||||||
tracing::info!("created hidden window to listen for monitor-related events");
|
tracing::info!("created hidden window to listen for monitor-related events");
|
||||||
|
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone(), win32_display_data::connected_displays_all) {
|
match handle_notifications(wm.clone(), win32_display_data::connected_displays_all) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -168,6 +169,7 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Re
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -219,27 +221,27 @@ where
|
|||||||
let mut should_update = false;
|
let mut should_update = false;
|
||||||
|
|
||||||
// Update work areas as necessary
|
// Update work areas as necessary
|
||||||
if let Ok(reference) = WindowsApi::monitor(monitor.id()) {
|
if let Ok(reference) = WindowsApi::monitor(monitor.id)
|
||||||
if reference.work_area_size() != monitor.work_area_size() {
|
&& reference.work_area_size != monitor.work_area_size
|
||||||
monitor.set_work_area_size(Rect {
|
{
|
||||||
left: reference.work_area_size().left,
|
monitor.work_area_size = Rect {
|
||||||
top: reference.work_area_size().top,
|
left: reference.work_area_size.left,
|
||||||
right: reference.work_area_size().right,
|
top: reference.work_area_size.top,
|
||||||
bottom: reference.work_area_size().bottom,
|
right: reference.work_area_size.right,
|
||||||
});
|
bottom: reference.work_area_size.bottom,
|
||||||
|
};
|
||||||
|
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if should_update {
|
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)?;
|
monitor.update_focused_workspace(offset)?;
|
||||||
border_manager::send_notification(None);
|
border_manager::send_notification(None);
|
||||||
} else {
|
} else {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"work areas match, reconciliation not required for {}",
|
"work areas match, reconciliation not required for {}",
|
||||||
monitor.device_id()
|
monitor.device_id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,25 +253,25 @@ where
|
|||||||
let mut should_update = false;
|
let mut should_update = false;
|
||||||
|
|
||||||
// Update sizes and work areas as necessary
|
// Update sizes and work areas as necessary
|
||||||
if let Ok(reference) = WindowsApi::monitor(monitor.id()) {
|
if let Ok(reference) = WindowsApi::monitor(monitor.id) {
|
||||||
if reference.work_area_size() != monitor.work_area_size() {
|
if reference.work_area_size != monitor.work_area_size {
|
||||||
monitor.set_work_area_size(Rect {
|
monitor.work_area_size = Rect {
|
||||||
left: reference.work_area_size().left,
|
left: reference.work_area_size.left,
|
||||||
top: reference.work_area_size().top,
|
top: reference.work_area_size.top,
|
||||||
right: reference.work_area_size().right,
|
right: reference.work_area_size.right,
|
||||||
bottom: reference.work_area_size().bottom,
|
bottom: reference.work_area_size.bottom,
|
||||||
});
|
};
|
||||||
|
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if reference.size() != monitor.size() {
|
if reference.size != monitor.size {
|
||||||
monitor.set_size(Rect {
|
monitor.size = Rect {
|
||||||
left: reference.size().left,
|
left: reference.size.left,
|
||||||
top: reference.size().top,
|
top: reference.size.top,
|
||||||
right: reference.size().right,
|
right: reference.size.right,
|
||||||
bottom: reference.size().bottom,
|
bottom: reference.size.bottom,
|
||||||
});
|
};
|
||||||
|
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
@@ -278,7 +280,7 @@ where
|
|||||||
if should_update {
|
if should_update {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
"updated monitor resolution/scaling for {}",
|
"updated monitor resolution/scaling for {}",
|
||||||
monitor.device_id()
|
monitor.device_id
|
||||||
);
|
);
|
||||||
|
|
||||||
monitor.update_focused_workspace(offset)?;
|
monitor.update_focused_workspace(offset)?;
|
||||||
@@ -286,7 +288,7 @@ where
|
|||||||
} else {
|
} else {
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
"resolutions match, reconciliation not required for {}",
|
"resolutions match, reconciliation not required for {}",
|
||||||
monitor.device_id()
|
monitor.device_id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,21 +313,21 @@ where
|
|||||||
for monitor in wm.monitors_mut() {
|
for monitor in wm.monitors_mut() {
|
||||||
for attached in &attached_devices {
|
for attached in &attached_devices {
|
||||||
let serial_number_ids_match = if let (Some(attached_snid), Some(m_snid)) =
|
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)
|
attached_snid.eq(m_snid)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if serial_number_ids_match || attached.device_id().eq(monitor.device_id()) {
|
if serial_number_ids_match || attached.device_id.eq(&monitor.device_id) {
|
||||||
monitor.set_id(attached.id());
|
monitor.id = attached.id;
|
||||||
monitor.set_device(attached.device().clone());
|
monitor.device = attached.device.clone();
|
||||||
monitor.set_device_id(attached.device_id().clone());
|
monitor.device_id = attached.device_id.clone();
|
||||||
monitor.set_serial_number_id(attached.serial_number_id().clone());
|
monitor.serial_number_id = attached.serial_number_id.clone();
|
||||||
monitor.set_name(attached.name().clone());
|
monitor.name = attached.name.clone();
|
||||||
monitor.set_size(*attached.size());
|
monitor.size = attached.size;
|
||||||
monitor.set_work_area_size(*attached.work_area_size());
|
monitor.work_area_size = attached.work_area_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,13 +361,13 @@ where
|
|||||||
|
|
||||||
for (m_idx, m) in wm.monitors().iter().enumerate() {
|
for (m_idx, m) in wm.monitors().iter().enumerate() {
|
||||||
if !attached_devices.iter().any(|attached| {
|
if !attached_devices.iter().any(|attached| {
|
||||||
attached.serial_number_id().eq(m.serial_number_id())
|
attached.serial_number_id.eq(&m.serial_number_id)
|
||||||
|| attached.device_id().eq(m.device_id())
|
|| attached.device_id.eq(&m.device_id)
|
||||||
}) {
|
}) {
|
||||||
let id = m
|
let id = m
|
||||||
.serial_number_id()
|
.serial_number_id
|
||||||
.as_ref()
|
.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());
|
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);
|
windows_to_remove.push(maximized.hwnd);
|
||||||
// Minimize the focused window since Windows might try
|
// Minimize the focused window since Windows might try
|
||||||
// to move it to another monitor if it was focused.
|
// 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() {
|
for window in container.windows() {
|
||||||
windows_to_remove.push(window.hwnd);
|
windows_to_remove.push(window.hwnd);
|
||||||
}
|
}
|
||||||
@@ -440,10 +442,10 @@ where
|
|||||||
// the user set as preference as the id.
|
// the user set as preference as the id.
|
||||||
let dip = DISPLAY_INDEX_PREFERENCES.read();
|
let dip = DISPLAY_INDEX_PREFERENCES.read();
|
||||||
let mut dip_ids = dip.values();
|
let mut dip_ids = dip.values();
|
||||||
let preferred_id = if dip_ids.any(|id| id == m.device_id()) {
|
let preferred_id = if dip_ids.any(|id| id.eq(&m.device_id)) {
|
||||||
m.device_id().clone()
|
m.device_id.clone()
|
||||||
} else if dip_ids.any(|id| Some(id) == m.serial_number_id().as_ref()) {
|
} else if dip_ids.any(|id| Some(id) == m.serial_number_id.as_ref()) {
|
||||||
m.serial_number_id().clone().unwrap_or_default()
|
m.serial_number_id.clone().unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
@@ -458,8 +460,8 @@ where
|
|||||||
// After we have cached them, remove them from our state
|
// After we have cached them, remove them from our state
|
||||||
wm.monitors_mut().retain(|m| {
|
wm.monitors_mut().retain(|m| {
|
||||||
!newly_removed_displays.iter().any(|id| {
|
!newly_removed_displays.iter().any(|id| {
|
||||||
m.serial_number_id().as_ref().is_some_and(|sn| sn == id)
|
m.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|
||||||
|| m.device_id() == id
|
|| m.device_id.eq(id)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -490,7 +492,7 @@ where
|
|||||||
let post_removal_device_ids = wm
|
let post_removal_device_ids = wm
|
||||||
.monitors()
|
.monitors()
|
||||||
.iter()
|
.iter()
|
||||||
.map(Monitor::device_id)
|
.map(|m| &m.device_id)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@@ -513,21 +515,23 @@ where
|
|||||||
|
|
||||||
// Look in the updated state for new monitors
|
// Look in the updated state for new monitors
|
||||||
for (i, m) in wm.monitors_mut().iter_mut().enumerate() {
|
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
|
// We identify a new monitor when we encounter a new device id
|
||||||
if !post_removal_device_ids.contains(device_id) {
|
if !post_removal_device_ids.contains(device_id) {
|
||||||
let mut cache_hit = false;
|
let mut cache_hit = false;
|
||||||
let mut cached_id = String::new();
|
let mut cached_id = String::new();
|
||||||
// Check if that device id exists in the cache for this session
|
// 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
|
if let Some((id, cached)) = monitor_cache.get_key_value(device_id).or(m
|
||||||
.serial_number_id()
|
.serial_number_id
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|sn| monitor_cache.get_key_value(sn)))
|
.and_then(|sn| monitor_cache.get_key_value(sn)))
|
||||||
{
|
{
|
||||||
cache_hit = true;
|
cache_hit = true;
|
||||||
cached_id = id.clone();
|
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
|
// If it does, update the cached monitor info with the new one and
|
||||||
// load the cached monitor removing any window that has since been
|
// 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()
|
if window.exe().is_err()
|
||||||
|| known_hwnds.contains_key(&window.hwnd)
|
|| known_hwnds.contains_key(&window.hwnd)
|
||||||
{
|
{
|
||||||
workspace.set_maximized_window(None);
|
workspace.maximized_window = None;
|
||||||
} else if is_focused_workspace {
|
} else if is_focused_workspace {
|
||||||
WindowsApi::restore_window(window.hwnd);
|
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| {
|
container.windows_mut().retain(|window| {
|
||||||
window.exe().is_ok()
|
window.exe().is_ok()
|
||||||
&& !known_hwnds.contains_key(&window.hwnd)
|
&& !known_hwnds.contains_key(&window.hwnd)
|
||||||
});
|
});
|
||||||
|
|
||||||
if container.windows().is_empty() {
|
if container.windows().is_empty() {
|
||||||
workspace.set_monocle_container(None);
|
workspace.monocle_container = None;
|
||||||
} else if is_focused_workspace {
|
} else if is_focused_workspace {
|
||||||
if let Some(window) = container.focused_window() {
|
if let Some(window) = container.focused_window() {
|
||||||
WindowsApi::restore_window(window.hwnd);
|
WindowsApi::restore_window(window.hwnd);
|
||||||
@@ -657,7 +661,7 @@ where
|
|||||||
let mut workspace_matching_rules =
|
let mut workspace_matching_rules =
|
||||||
WORKSPACE_MATCHING_RULES.lock();
|
WORKSPACE_MATCHING_RULES.lock();
|
||||||
if let Some(rules) = workspace
|
if let Some(rules) = workspace
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.workspace_rules.as_ref())
|
.and_then(|c| c.workspace_rules.as_ref())
|
||||||
{
|
{
|
||||||
@@ -672,7 +676,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rules) = workspace
|
if let Some(rules) = workspace
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.initial_workspace_rules.as_ref())
|
.and_then(|c| c.initial_workspace_rules.as_ref())
|
||||||
{
|
{
|
||||||
@@ -734,8 +738,8 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crossbeam_channel::bounded;
|
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
|
use crossbeam_channel::bounded;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use windows::Win32::Devices::Display::DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
|
use windows::Win32::Devices::Display::DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
|
||||||
@@ -801,7 +805,7 @@ mod tests {
|
|||||||
let wm = match WindowManager::new(receiver, Some(socket_path.clone())) {
|
let wm = match WindowManager::new(receiver, Some(socket_path.clone())) {
|
||||||
Ok(manager) => manager,
|
Ok(manager) => manager,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!("Failed to create WindowManager: {}", e);
|
panic!("Failed to create WindowManager: {e}");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -829,7 +833,7 @@ mod tests {
|
|||||||
Ok(notification) => {
|
Ok(notification) => {
|
||||||
assert_eq!(notification, MonitorNotification::ResolutionScalingChanged);
|
assert_eq!(notification, MonitorNotification::ResolutionScalingChanged);
|
||||||
}
|
}
|
||||||
Err(e) => panic!("Failed to receive MonitorNotification: {}", e),
|
Err(e) => panic!("Failed to receive MonitorNotification: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,7 +853,7 @@ mod tests {
|
|||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
let notification = match receiver.try_recv() {
|
let notification = match receiver.try_recv() {
|
||||||
Ok(notification) => notification,
|
Ok(notification) => notification,
|
||||||
Err(e) => panic!("Failed to receive MonitorNotification: {}", e),
|
Err(e) => panic!("Failed to receive MonitorNotification: {e}"),
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
notification,
|
notification,
|
||||||
@@ -960,7 +964,7 @@ mod tests {
|
|||||||
Ok(notification) => {
|
Ok(notification) => {
|
||||||
assert_eq!(notification, MonitorNotification::DisplayConnectionChange);
|
assert_eq!(notification, MonitorNotification::DisplayConnectionChange);
|
||||||
}
|
}
|
||||||
Err(e) => panic!("Failed to receive MonitorNotification: {}", e),
|
Err(e) => panic!("Failed to receive MonitorNotification: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1006,18 +1010,18 @@ mod tests {
|
|||||||
assert_eq!(monitors.len(), 1, "Expected one monitor");
|
assert_eq!(monitors.len(), 1, "Expected one monitor");
|
||||||
|
|
||||||
// hmonitor
|
// hmonitor
|
||||||
assert_eq!(monitors[0].id(), 1);
|
assert_eq!(monitors[0].id, 1);
|
||||||
|
|
||||||
// device name
|
// device name
|
||||||
assert_eq!(monitors[0].name(), &String::from("DISPLAY1"));
|
assert_eq!(monitors[0].name, String::from("DISPLAY1"));
|
||||||
|
|
||||||
// Device
|
// Device
|
||||||
assert_eq!(monitors[0].device(), &String::from("ABC123"));
|
assert_eq!(monitors[0].device, String::from("ABC123"));
|
||||||
|
|
||||||
// Device ID
|
// Device ID
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
monitors[0].device_id(),
|
monitors[0].device_id,
|
||||||
&String::from("ABC123-4&123456&0&UID0")
|
String::from("ABC123-4&123456&0&UID0")
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check monitor serial number id
|
// Check monitor serial number id
|
||||||
|
|||||||
+253
-270
File diff suppressed because it is too large
Load Diff
+105
-77
@@ -1,8 +1,9 @@
|
|||||||
use std::sync::atomic::Ordering;
|
use std::process::Command;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre;
|
||||||
use color_eyre::Result;
|
use color_eyre::eyre::OptionExt;
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
@@ -11,35 +12,57 @@ use crate::core::Rect;
|
|||||||
use crate::core::Sizing;
|
use crate::core::Sizing;
|
||||||
use crate::core::WindowContainerBehaviour;
|
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::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
|
use crate::VirtualDesktopNotification;
|
||||||
|
use crate::Window;
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
use crate::border_manager::BORDER_OFFSET;
|
use crate::border_manager::BORDER_OFFSET;
|
||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
use crate::current_virtual_desktop;
|
use crate::current_virtual_desktop;
|
||||||
use crate::notify_subscribers;
|
use crate::notify_subscribers;
|
||||||
|
use crate::splash;
|
||||||
|
use crate::splash::mdm_enrollment;
|
||||||
use crate::stackbar_manager;
|
use crate::stackbar_manager;
|
||||||
|
use crate::state::State;
|
||||||
use crate::transparency_manager;
|
use crate::transparency_manager;
|
||||||
use crate::window::should_act;
|
|
||||||
use crate::window::RuleDebug;
|
use crate::window::RuleDebug;
|
||||||
|
use crate::window::should_act;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::winevent::WinEvent;
|
use crate::winevent::WinEvent;
|
||||||
use crate::workspace::WorkspaceLayer;
|
use crate::workspace::WorkspaceLayer;
|
||||||
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]
|
#[tracing::instrument]
|
||||||
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
|
||||||
let receiver = wm.lock().incoming_events.clone();
|
let receiver = wm.lock().incoming_events.clone();
|
||||||
|
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
loop {
|
||||||
|
if let Ok((mdm, server)) = mdm_enrollment() {
|
||||||
|
#[allow(clippy::collapsible_if)]
|
||||||
|
if mdm && splash::should().map(|f| f.into()).unwrap_or(true) {
|
||||||
|
let mut args = vec!["splash".to_string()];
|
||||||
|
if let Some(server) = server {
|
||||||
|
args.push(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = Command::new("komorebic").args(&args).spawn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(14400));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
tracing::info!("listening");
|
tracing::info!("listening");
|
||||||
loop {
|
loop {
|
||||||
@@ -63,7 +86,7 @@ pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
|
|||||||
impl WindowManager {
|
impl WindowManager {
|
||||||
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
|
||||||
#[tracing::instrument(skip(self, event), fields(event = event.title(), winevent = event.winevent(), hwnd = event.hwnd()))]
|
#[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 {
|
if self.is_paused {
|
||||||
tracing::trace!("ignoring while paused");
|
tracing::trace!("ignoring while paused");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@@ -143,7 +166,9 @@ impl WindowManager {
|
|||||||
// to be consumed by integrating gui applications like bars to know
|
// to be consumed by integrating gui applications like bars to know
|
||||||
// when to hide visual components which don't make sense when not on
|
// when to hide visual components which don't make sense when not on
|
||||||
// komorebi's associated virtual desktop
|
// 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(
|
notify_subscribers(
|
||||||
Notification {
|
Notification {
|
||||||
event: NotificationEvent::VirtualDesktop(
|
event: NotificationEvent::VirtualDesktop(
|
||||||
@@ -163,7 +188,9 @@ impl WindowManager {
|
|||||||
// to be consumed by integrating gui applications like bars to know
|
// to be consumed by integrating gui applications like bars to know
|
||||||
// when to show visual components associated with komorebi's virtual
|
// when to show visual components associated with komorebi's virtual
|
||||||
// desktop
|
// 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(
|
notify_subscribers(
|
||||||
Notification {
|
Notification {
|
||||||
event: NotificationEvent::VirtualDesktop(
|
event: NotificationEvent::VirtualDesktop(
|
||||||
@@ -201,15 +228,14 @@ impl WindowManager {
|
|||||||
//
|
//
|
||||||
// This check ensures that we only update the focused monitor when the window
|
// 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.
|
// triggering monitor reconciliation is known to not be tied to a specific monitor.
|
||||||
if let Ok(class) = window.class() {
|
if let Ok(class) = window.class()
|
||||||
if class != "OleMainThreadWndClass"
|
&& class != "OleMainThreadWndClass"
|
||||||
&& self.focused_monitor_idx() != monitor_idx
|
&& self.focused_monitor_idx() != monitor_idx
|
||||||
{
|
{
|
||||||
self.focus_monitor(monitor_idx)?;
|
self.focus_monitor(monitor_idx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +327,11 @@ impl WindowManager {
|
|||||||
// don't want to trigger the full workspace updates when there are no managed
|
// don't want to trigger the full workspace updates when there are no managed
|
||||||
// containers - this makes floating windows on empty workspaces go into very
|
// containers - this makes floating windows on empty workspaces go into very
|
||||||
// annoying focus change loops which prevents users from interacting with them
|
// annoying focus change loops which prevents users from interacting with them
|
||||||
if !self.focused_workspace()?.containers().is_empty() {
|
if !matches!(
|
||||||
|
self.focused_workspace()?.layout,
|
||||||
|
Layout::Default(DefaultLayout::Scrolling)
|
||||||
|
) && !self.focused_workspace()?.containers().is_empty()
|
||||||
|
{
|
||||||
self.update_focused_workspace(self.mouse_follows_focus, false)?;
|
self.update_focused_workspace(self.mouse_follows_focus, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,13 +343,13 @@ impl WindowManager {
|
|||||||
|
|
||||||
match floating_window_idx {
|
match floating_window_idx {
|
||||||
None => {
|
None => {
|
||||||
if let Some(w) = workspace.maximized_window() {
|
if let Some(w) = &workspace.maximized_window
|
||||||
if w.hwnd == window.hwnd {
|
&& w.hwnd == window.hwnd
|
||||||
|
{
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(monocle) = workspace.monocle_container() {
|
if let Some(monocle) = &workspace.monocle_container {
|
||||||
if let Some(window) = monocle.focused_window() {
|
if let Some(window) = monocle.focused_window() {
|
||||||
window.focus(false)?;
|
window.focus(false)?;
|
||||||
}
|
}
|
||||||
@@ -327,11 +357,19 @@ impl WindowManager {
|
|||||||
workspace.focus_container_by_window(window.hwnd)?;
|
workspace.focus_container_by_window(window.hwnd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
workspace.layer = WorkspaceLayer::Tiling;
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
self.focused_workspace()?.layout,
|
||||||
|
Layout::Default(DefaultLayout::Scrolling)
|
||||||
|
) && !self.focused_workspace()?.containers().is_empty()
|
||||||
|
{
|
||||||
|
self.update_focused_workspace(self.mouse_follows_focus, false)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(idx) => {
|
Some(idx) => {
|
||||||
if let Some(_window) = workspace.floating_windows().get(idx) {
|
if let Some(_window) = workspace.floating_windows().get(idx) {
|
||||||
workspace.set_layer(WorkspaceLayer::Floating);
|
workspace.layer = WorkspaceLayer::Floating;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -375,13 +413,12 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd) {
|
if let Some((m_idx, w_idx)) = self.known_hwnds.get(&window.hwnd)
|
||||||
if let Some(focused_workspace_idx) = self
|
&& let Some(focused_workspace_idx) = self
|
||||||
.monitors()
|
.monitors()
|
||||||
.get(*m_idx)
|
.get(*m_idx)
|
||||||
.map(|m| m.focused_workspace_idx())
|
.map(|m| m.focused_workspace_idx())
|
||||||
{
|
&& *m_idx != self.focused_monitor_idx()
|
||||||
if *m_idx != self.focused_monitor_idx()
|
|
||||||
&& *w_idx != focused_workspace_idx
|
&& *w_idx != focused_workspace_idx
|
||||||
{
|
{
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
@@ -391,8 +428,6 @@ impl WindowManager {
|
|||||||
window.hide();
|
window.hide();
|
||||||
proceed = false;
|
proceed = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if proceed {
|
if proceed {
|
||||||
let behaviour = self.window_management_behaviour(
|
let behaviour = self.window_management_behaviour(
|
||||||
@@ -401,7 +436,7 @@ impl WindowManager {
|
|||||||
);
|
);
|
||||||
let workspace = self.focused_workspace_mut()?;
|
let workspace = self.focused_workspace_mut()?;
|
||||||
let workspace_contains_window = workspace.contains_window(window.hwnd);
|
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() {
|
if !workspace_contains_window && needs_reconciliation.is_none() {
|
||||||
let floating_applications = FLOATING_APPLICATIONS.lock();
|
let floating_applications = FLOATING_APPLICATIONS.lock();
|
||||||
@@ -444,11 +479,11 @@ impl WindowManager {
|
|||||||
let center_spawned_floats =
|
let center_spawned_floats =
|
||||||
placement.should_center() && workspace.tile;
|
placement.should_center() && workspace.tile;
|
||||||
workspace.floating_windows_mut().push_back(window);
|
workspace.floating_windows_mut().push_back(window);
|
||||||
workspace.set_layer(WorkspaceLayer::Floating);
|
workspace.layer = WorkspaceLayer::Floating;
|
||||||
if center_spawned_floats {
|
if center_spawned_floats {
|
||||||
let mut floating_window = window;
|
let mut floating_window = window;
|
||||||
floating_window.center(
|
floating_window.center(
|
||||||
&workspace.globals().work_area,
|
&workspace.globals.work_area,
|
||||||
placement.should_resize(),
|
placement.should_resize(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
@@ -457,17 +492,15 @@ impl WindowManager {
|
|||||||
match behaviour.current_behaviour {
|
match behaviour.current_behaviour {
|
||||||
WindowContainerBehaviour::Create => {
|
WindowContainerBehaviour::Create => {
|
||||||
workspace.new_container_for_window(window);
|
workspace.new_container_for_window(window);
|
||||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
workspace.layer = WorkspaceLayer::Tiling;
|
||||||
self.update_focused_workspace(false, false)?;
|
self.update_focused_workspace(false, false)?;
|
||||||
}
|
}
|
||||||
WindowContainerBehaviour::Append => {
|
WindowContainerBehaviour::Append => {
|
||||||
workspace
|
workspace
|
||||||
.focused_container_mut()
|
.focused_container_mut()
|
||||||
.ok_or_else(|| {
|
.ok_or_eyre("there is no focused container")?
|
||||||
anyhow!("there is no focused container")
|
|
||||||
})?
|
|
||||||
.add_window(window);
|
.add_window(window);
|
||||||
workspace.set_layer(WorkspaceLayer::Tiling);
|
workspace.layer = WorkspaceLayer::Tiling;
|
||||||
self.update_focused_workspace(true, false)?;
|
self.update_focused_workspace(true, false)?;
|
||||||
stackbar_manager::send_notification();
|
stackbar_manager::send_notification();
|
||||||
}
|
}
|
||||||
@@ -492,17 +525,15 @@ impl WindowManager {
|
|||||||
|
|
||||||
if workspace_contains_window {
|
if workspace_contains_window {
|
||||||
let mut monocle_window_event = false;
|
let mut monocle_window_event = false;
|
||||||
if let Some(ref monocle) = monocle_container {
|
if let Some(ref monocle) = monocle_container
|
||||||
if let Some(monocle_window) = monocle.focused_window() {
|
&& let Some(monocle_window) = monocle.focused_window()
|
||||||
if monocle_window.hwnd == window.hwnd {
|
&& monocle_window.hwnd == window.hwnd
|
||||||
|
{
|
||||||
monocle_window_event = true;
|
monocle_window_event = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let workspace = self.focused_workspace()?;
|
let workspace = self.focused_workspace()?;
|
||||||
if !(monocle_window_event
|
if !(monocle_window_event || workspace.layer != WorkspaceLayer::Tiling)
|
||||||
|| workspace.layer() != &WorkspaceLayer::Tiling)
|
|
||||||
&& monocle_container.is_some()
|
&& monocle_container.is_some()
|
||||||
{
|
{
|
||||||
window.hide();
|
window.hide();
|
||||||
@@ -515,7 +546,7 @@ impl WindowManager {
|
|||||||
let monitor_idx = self.focused_monitor_idx();
|
let monitor_idx = self.focused_monitor_idx();
|
||||||
let workspace_idx = self
|
let workspace_idx = self
|
||||||
.focused_monitor()
|
.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();
|
.focused_workspace_idx();
|
||||||
|
|
||||||
WindowsApi::bring_window_to_top(window.hwnd)?;
|
WindowsApi::bring_window_to_top(window.hwnd)?;
|
||||||
@@ -533,19 +564,19 @@ impl WindowManager {
|
|||||||
|
|
||||||
// If the window handles don't match then something went wrong and the pending move
|
// 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.
|
// is not related to this current move, if so abort this operation.
|
||||||
if let Some((_, _, w_hwnd)) = pending {
|
if let Some((_, _, w_hwnd)) = pending
|
||||||
if w_hwnd != window.hwnd {
|
&& w_hwnd != window.hwnd
|
||||||
|
{
|
||||||
color_eyre::eyre::bail!(
|
color_eyre::eyre::bail!(
|
||||||
"window handles for move operation don't match: {} != {}",
|
"window handles for move operation don't match: {} != {}",
|
||||||
w_hwnd,
|
w_hwnd,
|
||||||
window.hwnd
|
window.hwnd
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let target_monitor_idx = self
|
let target_monitor_idx = self
|
||||||
.monitor_idx_from_current_pos()
|
.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_monitor_idx = self.focused_monitor_idx();
|
||||||
let focused_workspace_idx = self.focused_workspace_idx().unwrap_or_default();
|
let focused_workspace_idx = self.focused_workspace_idx().unwrap_or_default();
|
||||||
@@ -556,7 +587,7 @@ impl WindowManager {
|
|||||||
let focused_container_idx = workspace.focused_container_idx();
|
let focused_container_idx = workspace.focused_container_idx();
|
||||||
let new_position = WindowsApi::window_rect(window.hwnd)?;
|
let new_position = WindowsApi::window_rect(window.hwnd)?;
|
||||||
let old_position = *workspace
|
let old_position = *workspace
|
||||||
.latest_layout()
|
.latest_layout
|
||||||
.get(focused_container_idx)
|
.get(focused_container_idx)
|
||||||
// If the move was to another monitor with an empty workspace, the
|
// If the move was to another monitor with an empty workspace, the
|
||||||
// workspace here will refer to that empty workspace, which won't
|
// workspace here will refer to that empty workspace, which won't
|
||||||
@@ -568,11 +599,11 @@ impl WindowManager {
|
|||||||
// This will be true if we have moved to another monitor
|
// This will be true if we have moved to another monitor
|
||||||
let mut moved_across_monitors = false;
|
let mut moved_across_monitors = false;
|
||||||
|
|
||||||
if let Some((m_idx, _)) = self.known_hwnds.get(&window.hwnd) {
|
if let Some((m_idx, _)) = self.known_hwnds.get(&window.hwnd)
|
||||||
if *m_idx != target_monitor_idx {
|
&& *m_idx != target_monitor_idx
|
||||||
|
{
|
||||||
moved_across_monitors = true;
|
moved_across_monitors = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((origin_monitor_idx, origin_workspace_idx, _)) = pending {
|
if let Some((origin_monitor_idx, origin_workspace_idx, _)) = pending {
|
||||||
// If we didn't move to another monitor with an empty workspace, it is
|
// If we didn't move to another monitor with an empty workspace, it is
|
||||||
@@ -589,10 +620,10 @@ impl WindowManager {
|
|||||||
let origin_workspace = self
|
let origin_workspace = self
|
||||||
.monitors()
|
.monitors()
|
||||||
.get(origin_monitor_idx)
|
.get(origin_monitor_idx)
|
||||||
.ok_or_else(|| anyhow!("cannot get monitor idx"))?
|
.ok_or_eyre("cannot get monitor idx")?
|
||||||
.workspaces()
|
.workspaces()
|
||||||
.get(origin_workspace_idx)
|
.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);
|
let managed_window = origin_workspace.contains_window(window.hwnd);
|
||||||
|
|
||||||
@@ -604,7 +635,7 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let workspace = self.focused_workspace_mut()?;
|
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
|
|| moved_across_monitors
|
||||||
{
|
{
|
||||||
let resize = Rect {
|
let resize = Rect {
|
||||||
@@ -632,17 +663,15 @@ impl WindowManager {
|
|||||||
let target_workspace_idx = self
|
let target_workspace_idx = self
|
||||||
.monitors()
|
.monitors()
|
||||||
.get(target_monitor_idx)
|
.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();
|
.focused_workspace_idx();
|
||||||
|
|
||||||
let target_container_idx = self
|
let target_container_idx = self
|
||||||
.monitors()
|
.monitors()
|
||||||
.get(target_monitor_idx)
|
.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()
|
.focused_workspace()
|
||||||
.ok_or_else(|| {
|
.ok_or_eyre("there is no focused workspace for this monitor")?
|
||||||
anyhow!("there is no focused workspace for this monitor")
|
|
||||||
})?
|
|
||||||
.container_idx_from_current_point()
|
.container_idx_from_current_point()
|
||||||
// Default to 0 in the case of an empty workspace
|
// Default to 0 in the case of an empty workspace
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
@@ -662,7 +691,7 @@ impl WindowManager {
|
|||||||
let origin_monitor = self
|
let origin_monitor = self
|
||||||
.monitors_mut()
|
.monitors_mut()
|
||||||
.get_mut(origin_monitor_idx)
|
.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)?;
|
origin_monitor.focus_workspace(origin_workspace_idx)?;
|
||||||
self.update_focused_workspace(false, false)?;
|
self.update_focused_workspace(false, false)?;
|
||||||
|
|
||||||
@@ -670,7 +699,7 @@ impl WindowManager {
|
|||||||
let target_monitor = self
|
let target_monitor = self
|
||||||
.monitors_mut()
|
.monitors_mut()
|
||||||
.get_mut(target_monitor_idx)
|
.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)?;
|
target_monitor.focus_workspace(target_workspace_idx)?;
|
||||||
self.update_focused_workspace(false, false)?;
|
self.update_focused_workspace(false, false)?;
|
||||||
|
|
||||||
@@ -822,7 +851,7 @@ impl WindowManager {
|
|||||||
.and_then(|m| m.workspaces().get(*ws_idx))
|
.and_then(|m| m.workspaces().get(*ws_idx))
|
||||||
{
|
{
|
||||||
if let Some(monocle_with_window) = target_workspace
|
if let Some(monocle_with_window) = target_workspace
|
||||||
.monocle_container()
|
.monocle_container
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|m| m.contains_window(window.hwnd).then_some(m))
|
.and_then(|m| m.contains_window(window.hwnd).then_some(m))
|
||||||
{
|
{
|
||||||
@@ -835,8 +864,8 @@ impl WindowManager {
|
|||||||
|
|
||||||
if let Some(target_container) =
|
if let Some(target_container) =
|
||||||
c_idx.and_then(|c_idx| target_workspace.containers().get(c_idx))
|
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!(
|
tracing::debug!(
|
||||||
"Needs reconciliation within a stack on the focused workspace"
|
"Needs reconciliation within a stack on the focused workspace"
|
||||||
);
|
);
|
||||||
@@ -844,7 +873,6 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
tracing::debug!("Needs reconciliation for a different monitor/workspace pair");
|
tracing::debug!("Needs reconciliation for a different monitor/workspace pair");
|
||||||
needs_reconciliation = Some((*m_idx, *ws_idx));
|
needs_reconciliation = Some((*m_idx, *ws_idx));
|
||||||
@@ -872,13 +900,13 @@ impl WindowManager {
|
|||||||
if let Some(monitor) = self.focused_monitor_mut() {
|
if let Some(monitor) = self.focused_monitor_mut() {
|
||||||
if ws_idx != monitor.focused_workspace_idx() {
|
if ws_idx != monitor.focused_workspace_idx() {
|
||||||
let previous_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)?;
|
monitor.focus_workspace(ws_idx)?;
|
||||||
}
|
}
|
||||||
if let Some(workspace) = monitor.focused_workspace_mut() {
|
if let Some(workspace) = monitor.focused_workspace_mut() {
|
||||||
let mut layer = WorkspaceLayer::Tiling;
|
let mut layer = WorkspaceLayer::Tiling;
|
||||||
if let Some((monocle, idx)) = workspace
|
if let Some((monocle, idx)) = workspace
|
||||||
.monocle_container_mut()
|
.monocle_container
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.and_then(|m| m.idx_for_window(window.hwnd).map(|i| (m, i)))
|
.and_then(|m| m.idx_for_window(window.hwnd).map(|i| (m, i)))
|
||||||
{
|
{
|
||||||
@@ -889,14 +917,14 @@ impl WindowManager {
|
|||||||
.any(|w| w.hwnd == window.hwnd)
|
.any(|w| w.hwnd == window.hwnd)
|
||||||
{
|
{
|
||||||
layer = WorkspaceLayer::Floating;
|
layer = WorkspaceLayer::Floating;
|
||||||
} else if !workspace
|
} else if workspace
|
||||||
.maximized_window()
|
.maximized_window
|
||||||
.is_some_and(|w| w.hwnd == window.hwnd)
|
.is_none_or(|w| w.hwnd != window.hwnd)
|
||||||
{
|
{
|
||||||
// If the window is the maximized window do nothing, else we
|
// If the window is the maximized window do nothing, else we
|
||||||
// reintegrate the monocle if it exists and then focus the
|
// reintegrate the monocle if it exists and then focus the
|
||||||
// container
|
// container
|
||||||
if workspace.monocle_container().is_some() {
|
if workspace.monocle_container.is_some() {
|
||||||
tracing::info!("disabling monocle");
|
tracing::info!("disabling monocle");
|
||||||
for container in workspace.containers_mut() {
|
for container in workspace.containers_mut() {
|
||||||
container.restore();
|
container.restore();
|
||||||
@@ -908,7 +936,7 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
workspace.focus_container_by_window(window.hwnd)?;
|
workspace.focus_container_by_window(window.hwnd)?;
|
||||||
}
|
}
|
||||||
workspace.set_layer(layer);
|
workspace.layer = layer;
|
||||||
}
|
}
|
||||||
monitor.load_focused_workspace(mouse_follows_focus)?;
|
monitor.load_focused_workspace(mouse_follows_focus)?;
|
||||||
monitor.update_focused_workspace(offset)?;
|
monitor.update_focused_workspace(offset)?;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use winput::Action;
|
||||||
use winput::message_loop;
|
use winput::message_loop;
|
||||||
use winput::message_loop::Event;
|
use winput::message_loop::Event;
|
||||||
use winput::Action;
|
|
||||||
|
|
||||||
use crate::core::FocusFollowsMouseImplementation;
|
use crate::core::FocusFollowsMouseImplementation;
|
||||||
|
|
||||||
|
|||||||
+11
-7
@@ -1,15 +1,15 @@
|
|||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
use crate::border_manager;
|
use crate::DATA_DIR;
|
||||||
use crate::notify_subscribers;
|
use crate::HIDING_BEHAVIOUR;
|
||||||
use crate::winevent::WinEvent;
|
|
||||||
use crate::HidingBehaviour;
|
use crate::HidingBehaviour;
|
||||||
use crate::NotificationEvent;
|
use crate::NotificationEvent;
|
||||||
use crate::Window;
|
use crate::Window;
|
||||||
use crate::WindowManager;
|
use crate::WindowManager;
|
||||||
use crate::WindowManagerEvent;
|
use crate::WindowManagerEvent;
|
||||||
use crate::DATA_DIR;
|
use crate::border_manager;
|
||||||
use crate::HIDING_BEHAVIOUR;
|
use crate::notify_subscribers;
|
||||||
|
use crate::winevent::WinEvent;
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
@@ -55,7 +55,8 @@ pub fn listen_for_notifications(
|
|||||||
) {
|
) {
|
||||||
watch_for_orphans(known_hwnds);
|
watch_for_orphans(known_hwnds);
|
||||||
|
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone()) {
|
match handle_notifications(wm.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -64,6 +65,7 @@ pub fn listen_for_notifications(
|
|||||||
tracing::warn!("restarting failed thread: {}", error);
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +152,8 @@ fn watch_for_orphans(known_hwnds: HashMap<isize, (usize, usize)>) {
|
|||||||
*cache = known_hwnds;
|
*cache = known_hwnds;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match find_orphans() {
|
match find_orphans() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -163,6 +166,7 @@ fn watch_for_orphans(known_hwnds: HashMap<isize, (usize, usize)>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,10 +43,6 @@ impl<T> Ring<T> {
|
|||||||
pub fn focused_mut(&mut self) -> Option<&mut T> {
|
pub fn focused_mut(&mut self) -> Option<&mut T> {
|
||||||
self.elements.get_mut(self.focused)
|
self.elements.get_mut(self.focused)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn swap(&mut self, i: usize, j: usize) {
|
|
||||||
self.elements.swap(i, j);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_ring_elements {
|
macro_rules! impl_ring_elements {
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
use crate::DATA_DIR;
|
||||||
|
use crate::License;
|
||||||
|
use crate::PUBLIC_KEY;
|
||||||
|
use base64::Engine;
|
||||||
|
use base64::engine::general_purpose;
|
||||||
|
use chrono::Duration;
|
||||||
|
use chrono::TimeZone;
|
||||||
|
use chrono::Utc;
|
||||||
|
use color_eyre::eyre;
|
||||||
|
use color_eyre::eyre::OptionExt;
|
||||||
|
use ed25519_dalek::Verifier;
|
||||||
|
use ed25519_dalek::VerifyingKey;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
|
pub fn mdm_enrollment() -> eyre::Result<(bool, Option<String>)> {
|
||||||
|
let mut command = Command::new("dsregcmd");
|
||||||
|
command.args(["/status"]);
|
||||||
|
let stdout = command.output()?.stdout;
|
||||||
|
let output = std::str::from_utf8(&stdout)?;
|
||||||
|
if !output.contains("MdmUrl") {
|
||||||
|
return Ok((false, None));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut server = None;
|
||||||
|
|
||||||
|
for line in output.lines() {
|
||||||
|
if line.contains("MdmUrl") {
|
||||||
|
let line = line.trim().to_string();
|
||||||
|
server = Some(line.trim_start_matches("MdmUrl : ").to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((true, server))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_payload(raw: &str, fresh: bool) -> eyre::Result<bool> {
|
||||||
|
let mut validation_successful = false;
|
||||||
|
|
||||||
|
let payload = serde_json::from_str::<License>(raw)?;
|
||||||
|
|
||||||
|
let signature = ed25519_dalek::Signature::from_slice(
|
||||||
|
general_purpose::STANDARD
|
||||||
|
.decode(&payload.signature)?
|
||||||
|
.as_slice(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut value: serde_json::Value = serde_json::from_str(raw)?;
|
||||||
|
if let serde_json::Value::Object(ref mut map) = value {
|
||||||
|
map.remove("signature");
|
||||||
|
}
|
||||||
|
|
||||||
|
let message_to_verify = serde_json::to_string(&value)?;
|
||||||
|
let verifying_key = VerifyingKey::from_bytes(&PUBLIC_KEY)?;
|
||||||
|
|
||||||
|
if verifying_key
|
||||||
|
.verify(message_to_verify.as_bytes(), &signature)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
if fresh {
|
||||||
|
let timestamp = Utc
|
||||||
|
.timestamp_opt(payload.timestamp, 0)
|
||||||
|
.single()
|
||||||
|
.ok_or_eyre("invalid timestamp")?;
|
||||||
|
|
||||||
|
let valid_duration = Utc::now() - Duration::minutes(5);
|
||||||
|
|
||||||
|
if timestamp <= valid_duration {
|
||||||
|
tracing::debug!("individual commercial use license verification payload was stale");
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if payload.has_valid_subscription
|
||||||
|
&& let Some(current_end_period) = payload.current_end_period
|
||||||
|
{
|
||||||
|
let subscription_valid_until = Utc
|
||||||
|
.timestamp_opt(current_end_period, 0)
|
||||||
|
.single()
|
||||||
|
.ok_or_eyre("invalid timestamp")?;
|
||||||
|
|
||||||
|
if Utc::now() <= subscription_valid_until {
|
||||||
|
tracing::debug!(
|
||||||
|
"individual commercial use license verification - subscription valid until: {subscription_valid_until}",
|
||||||
|
);
|
||||||
|
|
||||||
|
validation_successful = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(validation_successful)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ValidationFeedback {
|
||||||
|
Successful(PathBuf),
|
||||||
|
Unsuccessful(String),
|
||||||
|
NoEmail,
|
||||||
|
NoConnectivity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ValidationFeedback> for bool {
|
||||||
|
fn from(value: ValidationFeedback) -> Self {
|
||||||
|
match value {
|
||||||
|
ValidationFeedback::Successful(_) => false,
|
||||||
|
|
||||||
|
ValidationFeedback::Unsuccessful(_)
|
||||||
|
| ValidationFeedback::NoEmail
|
||||||
|
| ValidationFeedback::NoConnectivity => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn should() -> eyre::Result<ValidationFeedback> {
|
||||||
|
let icul_validation = DATA_DIR.join("icul.validation");
|
||||||
|
if icul_validation.exists() {
|
||||||
|
tracing::debug!("found local individual commercial use license validation payload");
|
||||||
|
let raw_payload = std::fs::read_to_string(&icul_validation)?;
|
||||||
|
if is_valid_payload(&raw_payload, false)? {
|
||||||
|
return Ok(ValidationFeedback::Successful(icul_validation));
|
||||||
|
} else {
|
||||||
|
std::fs::remove_file(&icul_validation)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let icul = DATA_DIR.join("icul");
|
||||||
|
if !icul.exists() {
|
||||||
|
return Ok(ValidationFeedback::NoEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
let email = std::fs::read_to_string(icul)?;
|
||||||
|
tracing::debug!("found individual commercial use license email: {}", email);
|
||||||
|
|
||||||
|
let client = reqwest::blocking::Client::new();
|
||||||
|
let response = match client
|
||||||
|
.get("https://kw-icul.lgug2z.com")
|
||||||
|
.query(&[("email", email.trim())])
|
||||||
|
.send()
|
||||||
|
{
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!("{error}");
|
||||||
|
return Ok(ValidationFeedback::NoConnectivity);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let raw_payload = response.text()?;
|
||||||
|
if is_valid_payload(&raw_payload, true)? {
|
||||||
|
std::fs::write(&icul_validation, &raw_payload)?;
|
||||||
|
Ok(ValidationFeedback::Successful(icul_validation))
|
||||||
|
} else {
|
||||||
|
Ok(ValidationFeedback::Unsuccessful(raw_payload))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +1,26 @@
|
|||||||
mod stackbar;
|
mod stackbar;
|
||||||
|
|
||||||
|
use crate::DEFAULT_CONTAINER_PADDING;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use crate::WindowsApi;
|
||||||
use crate::container::Container;
|
use crate::container::Container;
|
||||||
use crate::core::StackbarLabel;
|
use crate::core::StackbarLabel;
|
||||||
use crate::core::StackbarMode;
|
use crate::core::StackbarMode;
|
||||||
use crate::stackbar_manager::stackbar::Stackbar;
|
use crate::stackbar_manager::stackbar::Stackbar;
|
||||||
use crate::WindowManager;
|
|
||||||
use crate::WindowsApi;
|
|
||||||
use crate::DEFAULT_CONTAINER_PADDING;
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::collections::hash_map::Entry;
|
|
||||||
use std::collections::HashMap;
|
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::AtomicBool;
|
||||||
use std::sync::atomic::AtomicI32;
|
use std::sync::atomic::AtomicI32;
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::sync::atomic::Ordering;
|
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_FONT_SIZE: AtomicI32 = AtomicI32::new(0); // 0 will produce the system default
|
||||||
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
|
pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white
|
||||||
@@ -28,7 +28,7 @@ pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947);
|
|||||||
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
|
pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray
|
||||||
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
|
pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40);
|
||||||
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200);
|
||||||
pub static STACKBAR_LABEL: AtomicCell<StackbarLabel> = AtomicCell::new(StackbarLabel::Process);
|
pub static STACKBAR_LABEL: AtomicCell<StackbarLabel> = AtomicCell::new(StackbarLabel::Title);
|
||||||
pub static STACKBAR_MODE: AtomicCell<StackbarMode> = AtomicCell::new(StackbarMode::Never);
|
pub static STACKBAR_MODE: AtomicCell<StackbarMode> = AtomicCell::new(StackbarMode::Never);
|
||||||
|
|
||||||
pub static STACKBAR_TEMPORARILY_DISABLED: AtomicBool = AtomicBool::new(false);
|
pub static STACKBAR_TEMPORARILY_DISABLED: AtomicBool = AtomicBool::new(false);
|
||||||
@@ -71,7 +71,8 @@ pub fn should_have_stackbar(window_count: usize) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone()) {
|
match handle_notifications(wm.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -80,6 +81,7 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
|||||||
tracing::warn!("restarting failed thread: {}", 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
|
// Only operate on the focused workspace of each monitor
|
||||||
if let Some(ws) = m.focused_workspace_mut() {
|
if let Some(ws) = m.focused_workspace_mut() {
|
||||||
// Workspaces with tiling disabled don't have stackbars
|
// Workspaces with tiling disabled don't have stackbars
|
||||||
if !ws.tile() {
|
if !ws.tile {
|
||||||
let mut to_remove = vec![];
|
let mut to_remove = vec![];
|
||||||
for (id, border) in stackbars.iter() {
|
for (id, border) in stackbars.iter() {
|
||||||
if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx {
|
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());
|
WindowsApi::is_zoomed(WindowsApi::foreground_window().unwrap_or_default());
|
||||||
|
|
||||||
// Handle the monocle container separately
|
// 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
|
// Destroy any stackbars associated with the focused workspace
|
||||||
let mut to_remove = vec![];
|
let mut to_remove = vec![];
|
||||||
for (id, stackbar) in stackbars.iter() {
|
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
|
let container_ids = ws
|
||||||
.containers()
|
.containers()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| c.id().clone())
|
.map(|c| c.id.clone())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut to_remove = 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
|
let container_padding = ws
|
||||||
.container_padding()
|
.container_padding
|
||||||
.unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume());
|
.unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume());
|
||||||
|
|
||||||
'containers: for container in ws.containers_mut() {
|
'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 !should_add_stackbar {
|
||||||
if let Some(stackbar) = stackbars.get(container.id()) {
|
if let Some(stackbar) = stackbars.get(&container.id) {
|
||||||
stackbar.destroy()?
|
stackbar.destroy()?
|
||||||
}
|
}
|
||||||
|
|
||||||
stackbars.remove(container.id());
|
stackbars.remove(&container.id);
|
||||||
stackbars_monitors.remove(container.id());
|
stackbars_monitors.remove(&container.id);
|
||||||
continue 'containers;
|
continue 'containers;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the stackbar entry for this container from the map or create one
|
// 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::Occupied(entry) => entry.into_mut(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
if let Ok(stackbar) = Stackbar::create(container.id()) {
|
if let Ok(stackbar) = Stackbar::create(&container.id) {
|
||||||
entry.insert(stackbar)
|
entry.insert(stackbar)
|
||||||
} else {
|
} else {
|
||||||
continue 'receiver;
|
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(
|
let rect = WindowsApi::window_rect(
|
||||||
container.focused_window().copied().unwrap_or_default().hwnd,
|
container.focused_window().copied().unwrap_or_default().hwnd,
|
||||||
|
|||||||
@@ -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_OFFSET;
|
||||||
use crate::border_manager::BORDER_WIDTH;
|
use crate::border_manager::BORDER_WIDTH;
|
||||||
use crate::border_manager::STYLE;
|
use crate::border_manager::STYLE;
|
||||||
@@ -5,7 +8,6 @@ use crate::container::Container;
|
|||||||
use crate::core::BorderStyle;
|
use crate::core::BorderStyle;
|
||||||
use crate::core::Rect;
|
use crate::core::Rect;
|
||||||
use crate::core::StackbarLabel;
|
use crate::core::StackbarLabel;
|
||||||
use crate::stackbar_manager::STACKBARS_CONTAINERS;
|
|
||||||
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
use crate::stackbar_manager::STACKBAR_FONT_FAMILY;
|
use crate::stackbar_manager::STACKBAR_FONT_FAMILY;
|
||||||
use crate::stackbar_manager::STACKBAR_FONT_SIZE;
|
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_HEIGHT;
|
||||||
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
use crate::stackbar_manager::STACKBAR_TAB_WIDTH;
|
||||||
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBARS_CONTAINERS;
|
||||||
use crate::windows_api;
|
use crate::windows_api;
|
||||||
use crate::WindowsApi;
|
|
||||||
use crate::DEFAULT_CONTAINER_PADDING;
|
|
||||||
use crate::WINDOWS_11;
|
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
use std::os::windows::ffi::OsStrExt;
|
use std::os::windows::ffi::OsStrExt;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use windows::core::PCWSTR;
|
|
||||||
use windows::Win32::Foundation::COLORREF;
|
use windows::Win32::Foundation::COLORREF;
|
||||||
use windows::Win32::Foundation::HINSTANCE;
|
use windows::Win32::Foundation::HINSTANCE;
|
||||||
use windows::Win32::Foundation::HWND;
|
use windows::Win32::Foundation::HWND;
|
||||||
@@ -33,45 +32,50 @@ use windows::Win32::Foundation::WPARAM;
|
|||||||
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
|
use windows::Win32::Graphics::Gdi::CreateFontIndirectW;
|
||||||
use windows::Win32::Graphics::Gdi::CreatePen;
|
use windows::Win32::Graphics::Gdi::CreatePen;
|
||||||
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
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::DeleteObject;
|
||||||
use windows::Win32::Graphics::Gdi::DrawTextW;
|
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::GetDC;
|
||||||
use windows::Win32::Graphics::Gdi::GetDeviceCaps;
|
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::Rectangle;
|
||||||
use windows::Win32::Graphics::Gdi::ReleaseDC;
|
use windows::Win32::Graphics::Gdi::ReleaseDC;
|
||||||
use windows::Win32::Graphics::Gdi::RoundRect;
|
use windows::Win32::Graphics::Gdi::RoundRect;
|
||||||
use windows::Win32::Graphics::Gdi::SelectObject;
|
use windows::Win32::Graphics::Gdi::SelectObject;
|
||||||
use windows::Win32::Graphics::Gdi::SetBkColor;
|
use windows::Win32::Graphics::Gdi::SetBkColor;
|
||||||
use windows::Win32::Graphics::Gdi::SetTextColor;
|
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::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::CreateWindowExW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetMessageW;
|
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::PostQuitMessage;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::SetCursor;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::MSG;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
|
use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::WM_SETCURSOR;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
|
||||||
|
use windows::core::PCWSTR;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Stackbar {
|
pub struct Stackbar {
|
||||||
@@ -312,6 +316,16 @@ impl Stackbar {
|
|||||||
) -> LRESULT {
|
) -> LRESULT {
|
||||||
unsafe {
|
unsafe {
|
||||||
match msg {
|
match msg {
|
||||||
|
WM_SETCURSOR => match LoadCursorW(None, IDC_ARROW) {
|
||||||
|
Ok(cursor) => {
|
||||||
|
SetCursor(Some(cursor));
|
||||||
|
LRESULT(0)
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::error!("{error}");
|
||||||
|
LRESULT(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
WM_LBUTTONDOWN => {
|
WM_LBUTTONDOWN => {
|
||||||
let stackbars_containers = STACKBARS_CONTAINERS.lock();
|
let stackbars_containers = STACKBARS_CONTAINERS.lock();
|
||||||
if let Some(container) = stackbars_containers.get(&(hwnd.0 as isize)) {
|
if let Some(container) = stackbars_containers.get(&(hwnd.0 as isize)) {
|
||||||
@@ -339,8 +353,8 @@ impl Stackbar {
|
|||||||
// stackbar, make sure we update its location so that it doesn't render
|
// 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
|
// on top of other tiles before eventually ending up in the correct
|
||||||
// tile
|
// tile
|
||||||
if index != focused_window_idx {
|
if index != focused_window_idx
|
||||||
if let Err(err) =
|
&& let Err(err) =
|
||||||
window.set_position(&focused_window_rect, false)
|
window.set_position(&focused_window_rect, false)
|
||||||
{
|
{
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
@@ -349,7 +363,6 @@ impl Stackbar {
|
|||||||
err
|
err
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Restore the window corresponding to the tab we have clicked
|
// Restore the window corresponding to the tab we have clicked
|
||||||
window.restore_with_border(false);
|
window.restore_with_border(false);
|
||||||
|
|||||||
@@ -0,0 +1,306 @@
|
|||||||
|
use crate::BorderColours;
|
||||||
|
use crate::BorderStyle;
|
||||||
|
use crate::CURRENT_VIRTUAL_DESKTOP;
|
||||||
|
use crate::CUSTOM_FFM;
|
||||||
|
use crate::DATA_DIR;
|
||||||
|
use crate::DISPLAY_INDEX_PREFERENCES;
|
||||||
|
use crate::DUPLICATE_MONITOR_SERIAL_IDS;
|
||||||
|
use crate::FocusFollowsMouseImplementation;
|
||||||
|
use crate::HIDING_BEHAVIOUR;
|
||||||
|
use crate::HOME_DIR;
|
||||||
|
use crate::HidingBehaviour;
|
||||||
|
use crate::IGNORE_IDENTIFIERS;
|
||||||
|
use crate::LAYERED_WHITELIST;
|
||||||
|
use crate::MANAGE_IDENTIFIERS;
|
||||||
|
use crate::MONITOR_INDEX_PREFERENCES;
|
||||||
|
use crate::MoveBehaviour;
|
||||||
|
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||||
|
use crate::OperationBehaviour;
|
||||||
|
use crate::REMOVE_TITLEBARS;
|
||||||
|
use crate::Rect;
|
||||||
|
use crate::StackbarLabel;
|
||||||
|
use crate::StackbarMode;
|
||||||
|
use crate::TRANSPARENCY_BLACKLIST;
|
||||||
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
|
use crate::WORKSPACE_MATCHING_RULES;
|
||||||
|
use crate::WindowContainerBehaviour;
|
||||||
|
use crate::WindowManager;
|
||||||
|
use crate::border_manager;
|
||||||
|
use crate::border_manager::STYLE;
|
||||||
|
use crate::config_generation::MatchingRule;
|
||||||
|
use crate::config_generation::WorkspaceMatchingRule;
|
||||||
|
use crate::monitor::Monitor;
|
||||||
|
use crate::ring::Ring;
|
||||||
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
|
use crate::stackbar_manager::STACKBAR_LABEL;
|
||||||
|
use crate::stackbar_manager::STACKBAR_MODE;
|
||||||
|
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::transparency_manager::TRANSPARENCY_ALPHA;
|
||||||
|
use crate::transparency_manager::TRANSPARENCY_ENABLED;
|
||||||
|
use crate::workspace::Workspace;
|
||||||
|
use komorebi_themes::colour::Colour;
|
||||||
|
use komorebi_themes::colour::Rgb;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
pub struct State {
|
||||||
|
pub monitors: Ring<Monitor>,
|
||||||
|
pub monitor_usr_idx_map: HashMap<usize, usize>,
|
||||||
|
pub is_paused: bool,
|
||||||
|
pub resize_delta: i32,
|
||||||
|
pub new_window_behaviour: WindowContainerBehaviour,
|
||||||
|
pub float_override: bool,
|
||||||
|
pub cross_monitor_move_behaviour: MoveBehaviour,
|
||||||
|
pub unmanaged_window_operation_behaviour: OperationBehaviour,
|
||||||
|
pub work_area_offset: Option<Rect>,
|
||||||
|
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
|
||||||
|
pub mouse_follows_focus: bool,
|
||||||
|
pub has_pending_raise_op: bool,
|
||||||
|
pub virtual_desktop_id: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
pub fn has_been_modified(&self, wm: &WindowManager) -> bool {
|
||||||
|
let new = Self::from(wm);
|
||||||
|
|
||||||
|
if self.monitors != new.monitors {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_paused != new.is_paused {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.new_window_behaviour != new.new_window_behaviour {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.float_override != new.float_override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.cross_monitor_move_behaviour != new.cross_monitor_move_behaviour {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.unmanaged_window_operation_behaviour != new.unmanaged_window_operation_behaviour {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.work_area_offset != new.work_area_offset {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.focus_follows_mouse != new.focus_follows_mouse {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.mouse_follows_focus != new.mouse_follows_focus {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.has_pending_raise_op != new.has_pending_raise_op {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
pub struct GlobalState {
|
||||||
|
pub border_enabled: bool,
|
||||||
|
pub border_colours: BorderColours,
|
||||||
|
pub border_style: BorderStyle,
|
||||||
|
pub border_offset: i32,
|
||||||
|
pub border_width: i32,
|
||||||
|
pub stackbar_mode: StackbarMode,
|
||||||
|
pub stackbar_label: StackbarLabel,
|
||||||
|
pub stackbar_focused_text_colour: Colour,
|
||||||
|
pub stackbar_unfocused_text_colour: Colour,
|
||||||
|
pub stackbar_tab_background_colour: Colour,
|
||||||
|
pub stackbar_tab_width: i32,
|
||||||
|
pub stackbar_height: i32,
|
||||||
|
pub transparency_enabled: bool,
|
||||||
|
pub transparency_alpha: u8,
|
||||||
|
pub transparency_blacklist: Vec<MatchingRule>,
|
||||||
|
pub remove_titlebars: bool,
|
||||||
|
#[serde(alias = "float_identifiers")]
|
||||||
|
pub ignore_identifiers: Vec<MatchingRule>,
|
||||||
|
pub manage_identifiers: Vec<MatchingRule>,
|
||||||
|
pub layered_whitelist: Vec<MatchingRule>,
|
||||||
|
pub tray_and_multi_window_identifiers: Vec<MatchingRule>,
|
||||||
|
pub name_change_on_launch_identifiers: Vec<MatchingRule>,
|
||||||
|
pub monitor_index_preferences: HashMap<usize, Rect>,
|
||||||
|
pub display_index_preferences: HashMap<usize, String>,
|
||||||
|
pub ignored_duplicate_monitor_serial_ids: Vec<String>,
|
||||||
|
pub workspace_rules: Vec<WorkspaceMatchingRule>,
|
||||||
|
pub window_hiding_behaviour: HidingBehaviour,
|
||||||
|
pub configuration_dir: PathBuf,
|
||||||
|
pub data_dir: PathBuf,
|
||||||
|
pub custom_ffm: bool,
|
||||||
|
pub current_virtual_desktop_id: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GlobalState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
border_enabled: border_manager::BORDER_ENABLED.load(Ordering::SeqCst),
|
||||||
|
border_colours: BorderColours {
|
||||||
|
single: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::FOCUSED.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
stack: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::STACK.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
monocle: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::MONOCLE.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
floating: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::FLOATING.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
unfocused: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::UNFOCUSED.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
unfocused_locked: Option::from(Colour::Rgb(Rgb::from(
|
||||||
|
border_manager::UNFOCUSED_LOCKED.load(Ordering::SeqCst),
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
border_style: STYLE.load(),
|
||||||
|
border_offset: border_manager::BORDER_OFFSET.load(Ordering::SeqCst),
|
||||||
|
border_width: border_manager::BORDER_WIDTH.load(Ordering::SeqCst),
|
||||||
|
stackbar_mode: STACKBAR_MODE.load(),
|
||||||
|
stackbar_label: STACKBAR_LABEL.load(),
|
||||||
|
stackbar_focused_text_colour: Colour::Rgb(Rgb::from(
|
||||||
|
STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
|
||||||
|
)),
|
||||||
|
stackbar_unfocused_text_colour: Colour::Rgb(Rgb::from(
|
||||||
|
STACKBAR_UNFOCUSED_TEXT_COLOUR.load(Ordering::SeqCst),
|
||||||
|
)),
|
||||||
|
stackbar_tab_background_colour: Colour::Rgb(Rgb::from(
|
||||||
|
STACKBAR_TAB_BACKGROUND_COLOUR.load(Ordering::SeqCst),
|
||||||
|
)),
|
||||||
|
stackbar_tab_width: STACKBAR_TAB_WIDTH.load(Ordering::SeqCst),
|
||||||
|
stackbar_height: STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst),
|
||||||
|
transparency_enabled: TRANSPARENCY_ENABLED.load(Ordering::SeqCst),
|
||||||
|
transparency_alpha: TRANSPARENCY_ALPHA.load(Ordering::SeqCst),
|
||||||
|
transparency_blacklist: TRANSPARENCY_BLACKLIST.lock().clone(),
|
||||||
|
remove_titlebars: REMOVE_TITLEBARS.load(Ordering::SeqCst),
|
||||||
|
ignore_identifiers: IGNORE_IDENTIFIERS.lock().clone(),
|
||||||
|
manage_identifiers: MANAGE_IDENTIFIERS.lock().clone(),
|
||||||
|
layered_whitelist: LAYERED_WHITELIST.lock().clone(),
|
||||||
|
tray_and_multi_window_identifiers: TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock().clone(),
|
||||||
|
name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(),
|
||||||
|
monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(),
|
||||||
|
display_index_preferences: DISPLAY_INDEX_PREFERENCES.read().clone(),
|
||||||
|
ignored_duplicate_monitor_serial_ids: DUPLICATE_MONITOR_SERIAL_IDS.read().clone(),
|
||||||
|
workspace_rules: WORKSPACE_MATCHING_RULES.lock().clone(),
|
||||||
|
window_hiding_behaviour: *HIDING_BEHAVIOUR.lock(),
|
||||||
|
configuration_dir: HOME_DIR.clone(),
|
||||||
|
data_dir: DATA_DIR.clone(),
|
||||||
|
custom_ffm: CUSTOM_FFM.load(Ordering::SeqCst),
|
||||||
|
current_virtual_desktop_id: CURRENT_VIRTUAL_DESKTOP.lock().clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&WindowManager> for State {
|
||||||
|
fn from(wm: &WindowManager) -> Self {
|
||||||
|
// This is used to remove any information that doesn't need to be passed on to subscribers
|
||||||
|
// or to be shown with the `komorebic state` command. Currently it is only removing the
|
||||||
|
// `workspace_config` field from every workspace, but more stripping can be added later if
|
||||||
|
// needed.
|
||||||
|
let mut stripped_monitors = Ring::default();
|
||||||
|
*stripped_monitors.elements_mut() = wm
|
||||||
|
.monitors()
|
||||||
|
.iter()
|
||||||
|
.map(|monitor| Monitor {
|
||||||
|
id: monitor.id,
|
||||||
|
name: monitor.name.clone(),
|
||||||
|
device: monitor.device.clone(),
|
||||||
|
device_id: monitor.device_id.clone(),
|
||||||
|
serial_number_id: monitor.serial_number_id.clone(),
|
||||||
|
size: monitor.size,
|
||||||
|
work_area_size: monitor.work_area_size,
|
||||||
|
work_area_offset: monitor.work_area_offset,
|
||||||
|
window_based_work_area_offset: monitor.window_based_work_area_offset,
|
||||||
|
window_based_work_area_offset_limit: monitor.window_based_work_area_offset_limit,
|
||||||
|
workspaces: {
|
||||||
|
let mut ws = Ring::default();
|
||||||
|
*ws.elements_mut() = monitor
|
||||||
|
.workspaces()
|
||||||
|
.iter()
|
||||||
|
.map(|workspace| Workspace {
|
||||||
|
name: workspace.name.clone(),
|
||||||
|
containers: workspace.containers.clone(),
|
||||||
|
monocle_container: workspace.monocle_container.clone(),
|
||||||
|
monocle_container_restore_idx: workspace.monocle_container_restore_idx,
|
||||||
|
maximized_window: workspace.maximized_window,
|
||||||
|
maximized_window_restore_idx: workspace.maximized_window_restore_idx,
|
||||||
|
floating_windows: workspace.floating_windows.clone(),
|
||||||
|
layout: workspace.layout.clone(),
|
||||||
|
layout_options: workspace.layout_options,
|
||||||
|
layout_rules: workspace.layout_rules.clone(),
|
||||||
|
layout_flip: workspace.layout_flip,
|
||||||
|
workspace_padding: workspace.workspace_padding,
|
||||||
|
container_padding: workspace.container_padding,
|
||||||
|
latest_layout: workspace.latest_layout.clone(),
|
||||||
|
resize_dimensions: workspace.resize_dimensions.clone(),
|
||||||
|
tile: workspace.tile,
|
||||||
|
work_area_offset: workspace.work_area_offset,
|
||||||
|
apply_window_based_work_area_offset: workspace
|
||||||
|
.apply_window_based_work_area_offset,
|
||||||
|
window_container_behaviour: workspace.window_container_behaviour,
|
||||||
|
window_container_behaviour_rules: workspace
|
||||||
|
.window_container_behaviour_rules
|
||||||
|
.clone(),
|
||||||
|
float_override: workspace.float_override,
|
||||||
|
layer: workspace.layer,
|
||||||
|
floating_layer_behaviour: workspace.floating_layer_behaviour,
|
||||||
|
globals: workspace.globals,
|
||||||
|
wallpaper: workspace.wallpaper.clone(),
|
||||||
|
workspace_config: None,
|
||||||
|
})
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
ws.focus(monitor.workspaces.focused_idx());
|
||||||
|
ws
|
||||||
|
},
|
||||||
|
last_focused_workspace: monitor.last_focused_workspace,
|
||||||
|
workspace_names: monitor.workspace_names.clone(),
|
||||||
|
container_padding: monitor.container_padding,
|
||||||
|
workspace_padding: monitor.workspace_padding,
|
||||||
|
wallpaper: monitor.wallpaper.clone(),
|
||||||
|
floating_layer_behaviour: monitor.floating_layer_behaviour,
|
||||||
|
})
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
stripped_monitors.focus(wm.monitors.focused_idx());
|
||||||
|
|
||||||
|
Self {
|
||||||
|
monitors: stripped_monitors,
|
||||||
|
monitor_usr_idx_map: wm.monitor_usr_idx_map.clone(),
|
||||||
|
is_paused: wm.is_paused,
|
||||||
|
work_area_offset: wm.work_area_offset,
|
||||||
|
resize_delta: wm.resize_delta,
|
||||||
|
new_window_behaviour: wm.window_management_behaviour.current_behaviour,
|
||||||
|
float_override: wm.window_management_behaviour.float_override,
|
||||||
|
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
|
||||||
|
focus_follows_mouse: wm.focus_follows_mouse,
|
||||||
|
mouse_follows_focus: wm.mouse_follows_focus,
|
||||||
|
has_pending_raise_op: wm.has_pending_raise_op,
|
||||||
|
unmanaged_window_operation_behaviour: wm.unmanaged_window_operation_behaviour,
|
||||||
|
virtual_desktop_id: wm.virtual_desktop_id.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+159
-147
@@ -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_GLOBAL;
|
||||||
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
|
use crate::animation::ANIMATION_DURATION_PER_ANIMATION;
|
||||||
use crate::animation::ANIMATION_ENABLED_GLOBAL;
|
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_GLOBAL;
|
||||||
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
|
use crate::animation::ANIMATION_STYLE_PER_ANIMATION;
|
||||||
use crate::animation::DEFAULT_ANIMATION_FPS;
|
use crate::animation::DEFAULT_ANIMATION_FPS;
|
||||||
|
use crate::animation::PerAnimationPrefixConfig;
|
||||||
use crate::asc::ApplicationSpecificConfiguration;
|
use crate::asc::ApplicationSpecificConfiguration;
|
||||||
use crate::asc::AscApplicationRulesOrSchema;
|
use crate::asc::AscApplicationRulesOrSchema;
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
use crate::border_manager::ZOrder;
|
|
||||||
use crate::border_manager::IMPLEMENTATION;
|
use crate::border_manager::IMPLEMENTATION;
|
||||||
use crate::border_manager::STYLE;
|
use crate::border_manager::STYLE;
|
||||||
|
use crate::border_manager::ZOrder;
|
||||||
use crate::config_generation::WorkspaceMatchingRule;
|
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::AnimationStyle;
|
||||||
use crate::core::BorderImplementation;
|
use crate::core::BorderImplementation;
|
||||||
use crate::core::BorderStyle;
|
use crate::core::BorderStyle;
|
||||||
@@ -34,7 +59,13 @@ use crate::core::StackbarLabel;
|
|||||||
use crate::core::StackbarMode;
|
use crate::core::StackbarMode;
|
||||||
use crate::core::WindowContainerBehaviour;
|
use crate::core::WindowContainerBehaviour;
|
||||||
use crate::core::WindowManagementBehaviour;
|
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::current_virtual_desktop;
|
||||||
|
use crate::default_layout::LayoutOptions;
|
||||||
use crate::monitor;
|
use crate::monitor;
|
||||||
use crate::monitor::Monitor;
|
use crate::monitor::Monitor;
|
||||||
use crate::monitor_reconciliator;
|
use crate::monitor_reconciliator;
|
||||||
@@ -56,37 +87,7 @@ use crate::window_manager::WindowManager;
|
|||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
use crate::workspace::Workspace;
|
use crate::workspace::Workspace;
|
||||||
use crate::AspectRatio;
|
use color_eyre::eyre;
|
||||||
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 crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use hotwatch::EventKind;
|
use hotwatch::EventKind;
|
||||||
use hotwatch::Hotwatch;
|
use hotwatch::Hotwatch;
|
||||||
@@ -100,8 +101,8 @@ use std::collections::HashSet;
|
|||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use uds_windows::UnixListener;
|
use uds_windows::UnixListener;
|
||||||
use uds_windows::UnixStream;
|
use uds_windows::UnixStream;
|
||||||
|
|
||||||
@@ -191,6 +192,9 @@ pub struct WorkspaceConfig {
|
|||||||
/// Layout (default: BSP)
|
/// Layout (default: BSP)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub layout: Option<DefaultLayout>,
|
pub layout: Option<DefaultLayout>,
|
||||||
|
/// Layout-specific options (default: None)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub layout_options: Option<LayoutOptions>,
|
||||||
/// END OF LIFE FEATURE: Custom Layout (default: None)
|
/// END OF LIFE FEATURE: Custom Layout (default: None)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[serde_as(as = "Option<ResolvedPathBuf>")]
|
#[serde_as(as = "Option<ResolvedPathBuf>")]
|
||||||
@@ -214,6 +218,9 @@ pub struct WorkspaceConfig {
|
|||||||
/// Permanent workspace application rules
|
/// Permanent workspace application rules
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub workspace_rules: Option<Vec<MatchingRule>>,
|
pub workspace_rules: Option<Vec<MatchingRule>>,
|
||||||
|
/// Workspace specific work area offset (default: None)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub work_area_offset: Option<Rect>,
|
||||||
/// Apply this monitor's window-based work area offset (default: true)
|
/// Apply this monitor's window-based work area offset (default: true)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub apply_window_based_work_area_offset: Option<bool>,
|
pub apply_window_based_work_area_offset: Option<bool>,
|
||||||
@@ -226,6 +233,9 @@ pub struct WorkspaceConfig {
|
|||||||
/// Enable or disable float override, which makes it so every new window opens in floating mode (default: false)
|
/// Enable or disable float override, which makes it so every new window opens in floating mode (default: false)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub float_override: Option<bool>,
|
pub float_override: Option<bool>,
|
||||||
|
/// Enable or disable tiling for the workspace (default: true)
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tile: Option<bool>,
|
||||||
/// Specify an axis on which to flip the selected layout (default: None)
|
/// Specify an axis on which to flip the selected layout (default: None)
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub layout_flip: Option<Axis>,
|
pub layout_flip: Option<Axis>,
|
||||||
@@ -240,7 +250,7 @@ pub struct WorkspaceConfig {
|
|||||||
impl From<&Workspace> for WorkspaceConfig {
|
impl From<&Workspace> for WorkspaceConfig {
|
||||||
fn from(value: &Workspace) -> Self {
|
fn from(value: &Workspace) -> Self {
|
||||||
let mut layout_rules = HashMap::new();
|
let mut layout_rules = HashMap::new();
|
||||||
for (threshold, layout) in value.layout_rules() {
|
for (threshold, layout) in &value.layout_rules {
|
||||||
match layout {
|
match layout {
|
||||||
Layout::Default(value) => {
|
Layout::Default(value) => {
|
||||||
layout_rules.insert(*threshold, *value);
|
layout_rules.insert(*threshold, *value);
|
||||||
@@ -251,14 +261,14 @@ impl From<&Workspace> for WorkspaceConfig {
|
|||||||
let layout_rules = (!layout_rules.is_empty()).then_some(layout_rules);
|
let layout_rules = (!layout_rules.is_empty()).then_some(layout_rules);
|
||||||
|
|
||||||
let mut window_container_behaviour_rules = HashMap::new();
|
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);
|
window_container_behaviour_rules.insert(*threshold, *behaviour);
|
||||||
}
|
}
|
||||||
|
|
||||||
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
||||||
let default_workspace_padding = DEFAULT_WORKSPACE_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 {
|
if container_padding == default_container_padding {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -266,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 {
|
if workspace_padding == default_workspace_padding {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -274,43 +284,48 @@ impl From<&Workspace> for WorkspaceConfig {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let tile = if value.tile { None } else { Some(false) };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
name: value
|
name: value
|
||||||
.name()
|
.name
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| String::from("unnamed")),
|
.unwrap_or_else(|| String::from("unnamed")),
|
||||||
layout: value
|
layout: value
|
||||||
.tile()
|
.tile
|
||||||
.then_some(match value.layout() {
|
.then_some(match value.layout {
|
||||||
Layout::Default(layout) => Option::from(*layout),
|
Layout::Default(layout) => Option::from(layout),
|
||||||
Layout::Custom(_) => None,
|
Layout::Custom(_) => None,
|
||||||
})
|
})
|
||||||
.flatten(),
|
.flatten(),
|
||||||
|
layout_options: value.layout_options,
|
||||||
custom_layout: value
|
custom_layout: value
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.custom_layout.clone()),
|
.and_then(|c| c.custom_layout.clone()),
|
||||||
layout_rules,
|
layout_rules,
|
||||||
custom_layout_rules: value
|
custom_layout_rules: value
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.custom_layout_rules.clone()),
|
.and_then(|c| c.custom_layout_rules.clone()),
|
||||||
container_padding,
|
container_padding,
|
||||||
workspace_padding,
|
workspace_padding,
|
||||||
initial_workspace_rules: value
|
initial_workspace_rules: value
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.initial_workspace_rules.clone()),
|
.and_then(|c| c.initial_workspace_rules.clone()),
|
||||||
workspace_rules: value
|
workspace_rules: value
|
||||||
.workspace_config()
|
.workspace_config
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|c| c.workspace_rules.clone()),
|
.and_then(|c| c.workspace_rules.clone()),
|
||||||
apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()),
|
work_area_offset: value.work_area_offset,
|
||||||
window_container_behaviour: *value.window_container_behaviour(),
|
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),
|
window_container_behaviour_rules: Option::from(window_container_behaviour_rules),
|
||||||
float_override: *value.float_override(),
|
float_override: value.float_override,
|
||||||
layout_flip: value.layout_flip(),
|
tile,
|
||||||
floating_layer_behaviour: value.floating_layer_behaviour(),
|
layout_flip: value.layout_flip,
|
||||||
|
floating_layer_behaviour: value.floating_layer_behaviour,
|
||||||
wallpaper: None,
|
wallpaper: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,7 +369,7 @@ impl From<&Monitor> for MonitorConfig {
|
|||||||
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
let default_container_padding = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst);
|
||||||
let default_workspace_padding = DEFAULT_WORKSPACE_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 {
|
if container_padding == default_container_padding {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -362,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 {
|
if workspace_padding == default_workspace_padding {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -372,13 +387,13 @@ impl From<&Monitor> for MonitorConfig {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
workspaces,
|
workspaces,
|
||||||
work_area_offset: value.work_area_offset(),
|
work_area_offset: value.work_area_offset,
|
||||||
window_based_work_area_offset: value.window_based_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()),
|
window_based_work_area_offset_limit: Some(value.window_based_work_area_offset_limit),
|
||||||
container_padding,
|
container_padding,
|
||||||
workspace_padding,
|
workspace_padding,
|
||||||
wallpaper: value.wallpaper().clone(),
|
wallpaper: value.wallpaper.clone(),
|
||||||
floating_layer_behaviour: value.floating_layer_behaviour(),
|
floating_layer_behaviour: value.floating_layer_behaviour,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,7 +412,7 @@ pub enum AppSpecificConfigurationPath {
|
|||||||
#[serde_with::serde_as]
|
#[serde_with::serde_as]
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
/// The `komorebi.json` static configuration file reference for `v0.1.37`
|
/// The `komorebi.json` static configuration file reference for `v0.1.39`
|
||||||
pub struct StaticConfig {
|
pub struct StaticConfig {
|
||||||
/// DEPRECATED from v0.1.22: no longer required
|
/// DEPRECATED from v0.1.22: no longer required
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -716,7 +731,9 @@ impl StaticConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if display {
|
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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,7 +758,9 @@ impl StaticConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if display {
|
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 (canonical, aliases) in map {
|
||||||
for alias in aliases {
|
for alias in aliases {
|
||||||
if raw.contains(alias) {
|
if raw.contains(alias) {
|
||||||
@@ -933,7 +952,7 @@ impl From<&WindowManager> for StaticConfig {
|
|||||||
|
|
||||||
impl StaticConfig {
|
impl StaticConfig {
|
||||||
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
|
#[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_TOGGLE_ASPECT_RATIO.lock() = self
|
||||||
.floating_window_aspect_ratio
|
.floating_window_aspect_ratio
|
||||||
.unwrap_or(AspectRatio::Predefined(PredefinedAspectRatio::Standard));
|
.unwrap_or(AspectRatio::Predefined(PredefinedAspectRatio::Standard));
|
||||||
@@ -1221,11 +1240,11 @@ impl StaticConfig {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_raw(raw: &str) -> Result<Self> {
|
pub fn read_raw(raw: &str) -> eyre::Result<Self> {
|
||||||
Ok(serde_json::from_str(raw)?)
|
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)?;
|
let content = std::fs::read_to_string(path)?;
|
||||||
serde_json::from_str(&content).map_err(Into::into)
|
serde_json::from_str(&content).map_err(Into::into)
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1254,7 @@ impl StaticConfig {
|
|||||||
path: &PathBuf,
|
path: &PathBuf,
|
||||||
incoming: Receiver<WindowManagerEvent>,
|
incoming: Receiver<WindowManagerEvent>,
|
||||||
unix_listener: Option<UnixListener>,
|
unix_listener: Option<UnixListener>,
|
||||||
) -> Result<WindowManager> {
|
) -> eyre::Result<WindowManager> {
|
||||||
let mut value = Self::read(path)?;
|
let mut value = Self::read(path)?;
|
||||||
value.apply_globals()?;
|
value.apply_globals()?;
|
||||||
|
|
||||||
@@ -1330,8 +1349,8 @@ impl StaticConfig {
|
|||||||
Ok(wm)
|
Ok(wm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn postload(path: &PathBuf, wm: &Arc<Mutex<WindowManager>>) -> Result<()> {
|
pub fn postload(path: &PathBuf, wm: &Arc<Mutex<WindowManager>>) -> eyre::Result<()> {
|
||||||
let value = Self::read(path)?;
|
let mut value = Self::read(path)?;
|
||||||
let mut wm = wm.lock();
|
let mut wm = wm.lock();
|
||||||
|
|
||||||
let configs_with_preference: Vec<_> =
|
let configs_with_preference: Vec<_> =
|
||||||
@@ -1342,19 +1361,18 @@ impl StaticConfig {
|
|||||||
workspace_matching_rules.clear();
|
workspace_matching_rules.clear();
|
||||||
drop(workspace_matching_rules);
|
drop(workspace_matching_rules);
|
||||||
|
|
||||||
|
let monitor_count = wm.monitors().len();
|
||||||
|
|
||||||
let offset = wm.work_area_offset;
|
let offset = wm.work_area_offset;
|
||||||
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
||||||
let preferred_config_idx = {
|
let preferred_config_idx = {
|
||||||
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
||||||
let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
|
|
||||||
(monitor
|
display_index_preferences.iter().find_map(|(c_idx, id)| {
|
||||||
.serial_number_id()
|
(monitor.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|
||||||
.as_ref()
|
|| monitor.device_id.eq(id))
|
||||||
.is_some_and(|sn| sn == id)
|
|
||||||
|| monitor.device_id() == id)
|
|
||||||
.then_some(*c_idx)
|
.then_some(*c_idx)
|
||||||
});
|
})
|
||||||
c_idx
|
|
||||||
};
|
};
|
||||||
let idx = preferred_config_idx.or({
|
let idx = preferred_config_idx.or({
|
||||||
// Monitor without preferred config idx.
|
// Monitor without preferred config idx.
|
||||||
@@ -1371,31 +1389,37 @@ impl StaticConfig {
|
|||||||
});
|
});
|
||||||
if let Some(monitor_config) = value
|
if let Some(monitor_config) = value
|
||||||
.monitors
|
.monitors
|
||||||
.as_ref()
|
.as_mut()
|
||||||
.and_then(|ms| idx.and_then(|i| ms.get(i)))
|
.and_then(|ms| idx.and_then(|i| ms.get_mut(i)))
|
||||||
{
|
{
|
||||||
if let Some(used_config_idx) = idx {
|
if let Some(used_config_idx) = idx {
|
||||||
configs_used.push(used_config_idx);
|
configs_used.push(used_config_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor.ensure_workspace_count(monitor_config.workspaces.len());
|
monitor.ensure_workspace_count(monitor_config.workspaces.len());
|
||||||
monitor.set_work_area_offset(monitor_config.work_area_offset);
|
monitor.work_area_offset = monitor_config.work_area_offset;
|
||||||
monitor.set_window_based_work_area_offset(
|
monitor.window_based_work_area_offset =
|
||||||
monitor_config.window_based_work_area_offset,
|
monitor_config.window_based_work_area_offset;
|
||||||
);
|
monitor.window_based_work_area_offset_limit = monitor_config
|
||||||
monitor.set_window_based_work_area_offset_limit(
|
|
||||||
monitor_config
|
|
||||||
.window_based_work_area_offset_limit
|
.window_based_work_area_offset_limit
|
||||||
.unwrap_or(1),
|
.unwrap_or(1);
|
||||||
);
|
monitor.container_padding = monitor_config.container_padding;
|
||||||
monitor.set_container_padding(monitor_config.container_padding);
|
monitor.workspace_padding = monitor_config.workspace_padding;
|
||||||
monitor.set_workspace_padding(monitor_config.workspace_padding);
|
monitor.wallpaper = monitor_config.wallpaper.clone();
|
||||||
monitor.set_wallpaper(monitor_config.wallpaper.clone());
|
monitor.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
|
||||||
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
|
||||||
|
|
||||||
monitor.update_workspaces_globals(offset);
|
monitor.update_workspaces_globals(offset);
|
||||||
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
|
for (j, ws) in monitor.workspaces_mut().iter_mut().enumerate() {
|
||||||
if let Some(workspace_config) = monitor_config.workspaces.get(j) {
|
if let Some(workspace_config) = monitor_config.workspaces.get_mut(j) {
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
workspace_config.layout = Some(DefaultLayout::Columns);
|
||||||
|
}
|
||||||
|
|
||||||
ws.load_static_config(workspace_config)?;
|
ws.load_static_config(workspace_config)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1404,9 +1428,9 @@ impl StaticConfig {
|
|||||||
// a copy of the monitor itself on the monitor cache if it is.
|
// a copy of the monitor itself on the monitor cache if it is.
|
||||||
if idx == preferred_config_idx {
|
if idx == preferred_config_idx {
|
||||||
let id = monitor
|
let id = monitor
|
||||||
.serial_number_id()
|
.serial_number_id
|
||||||
.as_ref()
|
.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());
|
monitor_reconciliator::insert_in_monitor_cache(id, monitor.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1466,18 +1490,14 @@ impl StaticConfig {
|
|||||||
);
|
);
|
||||||
|
|
||||||
m.ensure_workspace_count(monitor_config.workspaces.len());
|
m.ensure_workspace_count(monitor_config.workspaces.len());
|
||||||
m.set_work_area_offset(monitor_config.work_area_offset);
|
m.work_area_offset = monitor_config.work_area_offset;
|
||||||
m.set_window_based_work_area_offset(
|
m.window_based_work_area_offset = monitor_config.window_based_work_area_offset;
|
||||||
monitor_config.window_based_work_area_offset,
|
m.window_based_work_area_offset_limit = monitor_config
|
||||||
);
|
|
||||||
m.set_window_based_work_area_offset_limit(
|
|
||||||
monitor_config
|
|
||||||
.window_based_work_area_offset_limit
|
.window_based_work_area_offset_limit
|
||||||
.unwrap_or(1),
|
.unwrap_or(1);
|
||||||
);
|
m.container_padding = monitor_config.container_padding;
|
||||||
m.set_container_padding(monitor_config.container_padding);
|
m.workspace_padding = monitor_config.workspace_padding;
|
||||||
m.set_workspace_padding(monitor_config.workspace_padding);
|
m.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
|
||||||
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
|
||||||
|
|
||||||
m.update_workspaces_globals(offset);
|
m.update_workspaces_globals(offset);
|
||||||
|
|
||||||
@@ -1501,7 +1521,7 @@ impl StaticConfig {
|
|||||||
Ok(())
|
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)?;
|
let mut value = Self::read(path)?;
|
||||||
|
|
||||||
value.apply_globals()?;
|
value.apply_globals()?;
|
||||||
@@ -1518,15 +1538,12 @@ impl StaticConfig {
|
|||||||
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
||||||
let preferred_config_idx = {
|
let preferred_config_idx = {
|
||||||
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
||||||
let c_idx = display_index_preferences.iter().find_map(|(c_idx, id)| {
|
|
||||||
(monitor
|
display_index_preferences.iter().find_map(|(c_idx, id)| {
|
||||||
.serial_number_id()
|
(monitor.serial_number_id.as_ref().is_some_and(|sn| sn == id)
|
||||||
.as_ref()
|
|| monitor.device_id.eq(id))
|
||||||
.is_some_and(|sn| sn == id)
|
|
||||||
|| monitor.device_id() == id)
|
|
||||||
.then_some(*c_idx)
|
.then_some(*c_idx)
|
||||||
});
|
})
|
||||||
c_idx
|
|
||||||
};
|
};
|
||||||
let idx = preferred_config_idx.or({
|
let idx = preferred_config_idx.or({
|
||||||
// Monitor without preferred config idx.
|
// Monitor without preferred config idx.
|
||||||
@@ -1551,21 +1568,18 @@ impl StaticConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
monitor.ensure_workspace_count(monitor_config.workspaces.len());
|
monitor.ensure_workspace_count(monitor_config.workspaces.len());
|
||||||
if monitor.work_area_offset().is_none() {
|
if monitor.work_area_offset.is_none() {
|
||||||
monitor.set_work_area_offset(monitor_config.work_area_offset);
|
monitor.work_area_offset = monitor_config.work_area_offset;
|
||||||
}
|
}
|
||||||
monitor.set_window_based_work_area_offset(
|
monitor.window_based_work_area_offset =
|
||||||
monitor_config.window_based_work_area_offset,
|
monitor_config.window_based_work_area_offset;
|
||||||
);
|
monitor.window_based_work_area_offset_limit = monitor_config
|
||||||
monitor.set_window_based_work_area_offset_limit(
|
|
||||||
monitor_config
|
|
||||||
.window_based_work_area_offset_limit
|
.window_based_work_area_offset_limit
|
||||||
.unwrap_or(1),
|
.unwrap_or(1);
|
||||||
);
|
monitor.container_padding = monitor_config.container_padding;
|
||||||
monitor.set_container_padding(monitor_config.container_padding);
|
monitor.workspace_padding = monitor_config.workspace_padding;
|
||||||
monitor.set_workspace_padding(monitor_config.workspace_padding);
|
monitor.wallpaper = monitor_config.wallpaper.clone();
|
||||||
monitor.set_wallpaper(monitor_config.wallpaper.clone());
|
monitor.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
|
||||||
monitor.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
|
||||||
|
|
||||||
monitor.update_workspaces_globals(offset);
|
monitor.update_workspaces_globals(offset);
|
||||||
|
|
||||||
@@ -1579,9 +1593,9 @@ impl StaticConfig {
|
|||||||
// a copy of the monitor itself on the monitor cache if it is.
|
// a copy of the monitor itself on the monitor cache if it is.
|
||||||
if idx == preferred_config_idx {
|
if idx == preferred_config_idx {
|
||||||
let id = monitor
|
let id = monitor
|
||||||
.serial_number_id()
|
.serial_number_id
|
||||||
.as_ref()
|
.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());
|
monitor_reconciliator::insert_in_monitor_cache(id, monitor.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1641,18 +1655,14 @@ impl StaticConfig {
|
|||||||
);
|
);
|
||||||
|
|
||||||
m.ensure_workspace_count(monitor_config.workspaces.len());
|
m.ensure_workspace_count(monitor_config.workspaces.len());
|
||||||
m.set_work_area_offset(monitor_config.work_area_offset);
|
m.work_area_offset = monitor_config.work_area_offset;
|
||||||
m.set_window_based_work_area_offset(
|
m.window_based_work_area_offset = monitor_config.window_based_work_area_offset;
|
||||||
monitor_config.window_based_work_area_offset,
|
m.window_based_work_area_offset_limit = monitor_config
|
||||||
);
|
|
||||||
m.set_window_based_work_area_offset_limit(
|
|
||||||
monitor_config
|
|
||||||
.window_based_work_area_offset_limit
|
.window_based_work_area_offset_limit
|
||||||
.unwrap_or(1),
|
.unwrap_or(1);
|
||||||
);
|
m.container_padding = monitor_config.container_padding;
|
||||||
m.set_container_padding(monitor_config.container_padding);
|
m.workspace_padding = monitor_config.workspace_padding;
|
||||||
m.set_workspace_padding(monitor_config.workspace_padding);
|
m.floating_layer_behaviour = monitor_config.floating_layer_behaviour;
|
||||||
m.set_floating_layer_behaviour(monitor_config.floating_layer_behaviour);
|
|
||||||
|
|
||||||
m.update_workspaces_globals(offset);
|
m.update_workspaces_globals(offset);
|
||||||
|
|
||||||
@@ -1718,7 +1728,7 @@ fn populate_option(
|
|||||||
entry: &mut ApplicationConfiguration,
|
entry: &mut ApplicationConfiguration,
|
||||||
identifiers: &mut Vec<MatchingRule>,
|
identifiers: &mut Vec<MatchingRule>,
|
||||||
regex_identifiers: &mut HashMap<String, Regex>,
|
regex_identifiers: &mut HashMap<String, Regex>,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
if entry.identifier.matching_strategy.is_none() {
|
if entry.identifier.matching_strategy.is_none() {
|
||||||
entry.identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
|
entry.identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
|
||||||
}
|
}
|
||||||
@@ -1744,7 +1754,7 @@ fn populate_rules(
|
|||||||
matching_rules: &mut Vec<MatchingRule>,
|
matching_rules: &mut Vec<MatchingRule>,
|
||||||
identifiers: &mut Vec<MatchingRule>,
|
identifiers: &mut Vec<MatchingRule>,
|
||||||
regex_identifiers: &mut HashMap<String, Regex>,
|
regex_identifiers: &mut HashMap<String, Regex>,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
for matching_rule in matching_rules {
|
for matching_rule in matching_rules {
|
||||||
if !identifiers.contains(matching_rule) {
|
if !identifiers.contains(matching_rule) {
|
||||||
match matching_rule {
|
match matching_rule {
|
||||||
@@ -1790,7 +1800,7 @@ fn handle_asc_file(
|
|||||||
transparency_blacklist: &mut Vec<MatchingRule>,
|
transparency_blacklist: &mut Vec<MatchingRule>,
|
||||||
slow_application_identifiers: &mut Vec<MatchingRule>,
|
slow_application_identifiers: &mut Vec<MatchingRule>,
|
||||||
regex_identifiers: &mut HashMap<String, Regex>,
|
regex_identifiers: &mut HashMap<String, Regex>,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
match path.extension() {
|
match path.extension() {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ext) => match ext.to_string_lossy().to_string().as_str() {
|
Some(ext) => match ext.to_string_lossy().to_string().as_str() {
|
||||||
@@ -1918,7 +1928,7 @@ mod tests {
|
|||||||
let docs = vec![
|
let docs = vec![
|
||||||
"0.1.20", "0.1.21", "0.1.22", "0.1.23", "0.1.24", "0.1.25", "0.1.26", "0.1.27",
|
"0.1.20", "0.1.21", "0.1.22", "0.1.23", "0.1.24", "0.1.25", "0.1.26", "0.1.27",
|
||||||
"0.1.28", "0.1.29", "0.1.30", "0.1.31", "0.1.32", "0.1.33", "0.1.34", "0.1.35",
|
"0.1.28", "0.1.29", "0.1.30", "0.1.31", "0.1.32", "0.1.33", "0.1.34", "0.1.35",
|
||||||
"0.1.36",
|
"0.1.36", "0.1.37",
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut versions = vec![];
|
let mut versions = vec![];
|
||||||
@@ -1944,7 +1954,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn deserialize_custom_layout_rules() {
|
fn deserialize_custom_layout_rules() {
|
||||||
// set an environment variable for testing
|
// set an environment variable for testing
|
||||||
|
unsafe {
|
||||||
std::env::set_var("VAR", "VALUE");
|
std::env::set_var("VAR", "VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
let config = r#"
|
let config = r#"
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
#![deny(clippy::unwrap_used, clippy::expect_used)]
|
||||||
|
|
||||||
|
use crate::KomorebiTheme;
|
||||||
use crate::border_manager;
|
use crate::border_manager;
|
||||||
use crate::stackbar_manager;
|
use crate::stackbar_manager;
|
||||||
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR;
|
||||||
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR;
|
||||||
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR;
|
||||||
use crate::KomorebiTheme;
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::atomic::AtomicCell;
|
use crossbeam_utils::atomic::AtomicCell;
|
||||||
use komorebi_themes::colour::Colour;
|
|
||||||
use komorebi_themes::Base16Wrapper;
|
use komorebi_themes::Base16Wrapper;
|
||||||
|
use komorebi_themes::colour::Colour;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
pub struct Notification(KomorebiTheme);
|
pub struct Notification(KomorebiTheme);
|
||||||
|
|
||||||
@@ -51,7 +51,8 @@ pub fn send_notification(theme: KomorebiTheme) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_for_notifications() {
|
pub fn listen_for_notifications() {
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications() {
|
match handle_notifications() {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -60,6 +61,7 @@ pub fn listen_for_notifications() {
|
|||||||
tracing::warn!("restarting failed thread: {}", error);
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,20 @@ use crossbeam_channel::Receiver;
|
|||||||
use crossbeam_channel::Sender;
|
use crossbeam_channel::Sender;
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::atomic::AtomicU8;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::OnceLock;
|
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::Window;
|
||||||
use crate::WindowManager;
|
use crate::WindowManager;
|
||||||
use crate::WindowsApi;
|
use crate::WindowsApi;
|
||||||
use crate::REGEX_IDENTIFIERS;
|
use crate::should_act;
|
||||||
use crate::TRANSPARENCY_BLACKLIST;
|
|
||||||
|
|
||||||
pub static TRANSPARENCY_ENABLED: AtomicBool = AtomicBool::new(false);
|
pub static TRANSPARENCY_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||||
|
pub static TRANSPARENCY_ENABLED_OVERRIDE: AtomicBool = AtomicBool::new(false);
|
||||||
pub static TRANSPARENCY_ALPHA: AtomicU8 = AtomicU8::new(200);
|
pub static TRANSPARENCY_ALPHA: AtomicU8 = AtomicU8::new(200);
|
||||||
|
|
||||||
static KNOWN_HWNDS: OnceLock<Mutex<Vec<isize>>> = OnceLock::new();
|
static KNOWN_HWNDS: OnceLock<Mutex<Vec<isize>>> = OnceLock::new();
|
||||||
@@ -49,7 +50,8 @@ pub fn send_notification() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
||||||
std::thread::spawn(move || loop {
|
std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
match handle_notifications(wm.clone()) {
|
match handle_notifications(wm.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
tracing::warn!("restarting finished thread");
|
tracing::warn!("restarting finished thread");
|
||||||
@@ -58,6 +60,7 @@ pub fn listen_for_notifications(wm: Arc<Mutex<WindowManager>>) {
|
|||||||
tracing::warn!("restarting failed thread: {}", error);
|
tracing::warn!("restarting failed thread: {}", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +95,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
'workspaces: for (workspace_idx, ws) in m.workspaces().iter().enumerate() {
|
'workspaces: for (workspace_idx, ws) in m.workspaces().iter().enumerate() {
|
||||||
// Only operate on the focused workspace of each monitor
|
// Only operate on the focused workspace of each monitor
|
||||||
// Workspaces with tiling disabled don't have transparent windows
|
// 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() {
|
for window in ws.visible_windows().iter().flatten() {
|
||||||
if let Err(error) = window.opaque() {
|
if let Err(error) = window.opaque() {
|
||||||
let hwnd = window.hwnd;
|
let hwnd = window.hwnd;
|
||||||
@@ -104,7 +107,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Monocle container is never transparent
|
// 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 let Some(window) = monocle.focused_window() {
|
||||||
if monitor_idx == focused_monitor_idx {
|
if monitor_idx == focused_monitor_idx {
|
||||||
if let Err(error) = window.opaque() {
|
if let Err(error) = window.opaque() {
|
||||||
@@ -150,13 +153,14 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
for (window_idx, window) in c.windows().iter().enumerate() {
|
for (window_idx, window) in c.windows().iter().enumerate() {
|
||||||
if window_idx == focused_window_idx {
|
if window_idx == focused_window_idx {
|
||||||
let mut should_make_transparent = true;
|
let mut should_make_transparent = true;
|
||||||
if !transparency_blacklist.is_empty() {
|
if !transparency_blacklist.is_empty()
|
||||||
if let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (
|
&& let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (
|
||||||
window.title(),
|
window.title(),
|
||||||
window.exe(),
|
window.exe(),
|
||||||
window.class(),
|
window.class(),
|
||||||
window.path(),
|
window.path(),
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
let is_blacklisted = should_act(
|
let is_blacklisted = should_act(
|
||||||
&title,
|
&title,
|
||||||
&exe_name,
|
&exe_name,
|
||||||
@@ -169,13 +173,14 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
|||||||
|
|
||||||
should_make_transparent = !is_blacklisted;
|
should_make_transparent = !is_blacklisted;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if should_make_transparent {
|
if should_make_transparent {
|
||||||
match window.transparent() {
|
match window.transparent() {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let hwnd = foreground_hwnd;
|
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(..) => {
|
Ok(..) => {
|
||||||
known_hwnds.lock().push(window.hwnd);
|
known_hwnds.lock().push(window.hwnd);
|
||||||
|
|||||||
+80
-81
@@ -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::AnimationStyle;
|
||||||
use crate::FLOATING_APPLICATIONS;
|
use crate::FLOATING_APPLICATIONS;
|
||||||
use crate::FLOATING_WINDOW_TOGGLE_ASPECT_RATIO;
|
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_COMPENSATION_TIME;
|
||||||
use crate::SLOW_APPLICATION_IDENTIFIERS;
|
use crate::SLOW_APPLICATION_IDENTIFIERS;
|
||||||
use crate::WSL2_UI_PROCESSES;
|
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::eyre;
|
||||||
use color_eyre::Result;
|
|
||||||
use crossbeam_utils::atomic::AtomicConsume;
|
use crossbeam_utils::atomic::AtomicConsume;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde::Serializer;
|
use serde::Serializer;
|
||||||
|
use serde::ser::SerializeStruct;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@@ -128,7 +127,7 @@ impl Display for Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize 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
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
@@ -193,14 +192,14 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
|||||||
new_animation_key(MovementRenderDispatcher::PREFIX, self.hwnd.to_string())
|
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::STACKBAR_TEMPORARILY_DISABLED.store(true, Ordering::SeqCst);
|
||||||
stackbar_manager::send_notification();
|
stackbar_manager::send_notification();
|
||||||
|
|
||||||
Ok(())
|
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);
|
let new_rect = self.start_rect.lerp(self.target_rect, progress, self.style);
|
||||||
|
|
||||||
// we don't check WINDOW_HANDLING_BEHAVIOUR here because animations
|
// we don't check WINDOW_HANDLING_BEHAVIOUR here because animations
|
||||||
@@ -211,7 +210,7 @@ impl RenderDispatcher for MovementRenderDispatcher {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_render(&self) -> Result<()> {
|
fn post_render(&self) -> eyre::Result<()> {
|
||||||
// we don't add the async_window_pos flag here because animations
|
// we don't add the async_window_pos flag here because animations
|
||||||
// are always run on a separate thread
|
// are always run on a separate thread
|
||||||
WindowsApi::position_window(self.hwnd, &self.target_rect, self.top, false)?;
|
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())
|
new_animation_key(TransparencyRenderDispatcher::PREFIX, self.hwnd.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_render(&self) -> Result<()> {
|
fn pre_render(&self) -> eyre::Result<()> {
|
||||||
//transparent
|
//transparent
|
||||||
if !self.is_opaque {
|
if !self.is_opaque {
|
||||||
let window = Window::from(self.hwnd);
|
let window = Window::from(self.hwnd);
|
||||||
@@ -279,7 +278,7 @@ impl RenderDispatcher for TransparencyRenderDispatcher {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, progress: f64) -> Result<()> {
|
fn render(&self, progress: f64) -> eyre::Result<()> {
|
||||||
WindowsApi::set_transparent(
|
WindowsApi::set_transparent(
|
||||||
self.hwnd,
|
self.hwnd,
|
||||||
self.start_opacity
|
self.start_opacity
|
||||||
@@ -287,7 +286,7 @@ impl RenderDispatcher for TransparencyRenderDispatcher {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_render(&self) -> Result<()> {
|
fn post_render(&self) -> eyre::Result<()> {
|
||||||
//opaque
|
//opaque
|
||||||
if self.is_opaque {
|
if self.is_opaque {
|
||||||
let window = Window::from(self.hwnd);
|
let window = Window::from(self.hwnd);
|
||||||
@@ -346,7 +345,7 @@ impl Window {
|
|||||||
HWND(windows_api::as_ptr!(self.hwnd))
|
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 current_rect = WindowsApi::window_rect(self.hwnd)?;
|
||||||
let x_diff = target_area.left - current_area.left;
|
let x_diff = target_area.left - current_area.left;
|
||||||
let y_diff = target_area.top - current_area.top;
|
let y_diff = target_area.top - current_area.top;
|
||||||
@@ -413,7 +412,7 @@ impl Window {
|
|||||||
Ok(())
|
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 (target_width, target_height) = if resize {
|
||||||
let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO
|
let (aspect_ratio_width, aspect_ratio_height) = FLOATING_WINDOW_TOGGLE_ASPECT_RATIO
|
||||||
.lock()
|
.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)?;
|
let window_rect = WindowsApi::window_rect(self.hwnd)?;
|
||||||
|
|
||||||
if window_rect.eq(layout) {
|
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)
|
WindowsApi::close_window(self.hwnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,10 +565,11 @@ impl Window {
|
|||||||
WindowsApi::unmaximize_window(self.hwnd);
|
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 the target window is already focused, do nothing.
|
||||||
if let Ok(ihwnd) = WindowsApi::foreground_window() {
|
if let Ok(ihwnd) = WindowsApi::foreground_window()
|
||||||
if ihwnd == self.hwnd {
|
&& ihwnd == self.hwnd
|
||||||
|
{
|
||||||
// Center cursor in Window
|
// Center cursor in Window
|
||||||
if mouse_follows_focus {
|
if mouse_follows_focus {
|
||||||
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd)?)?;
|
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd)?)?;
|
||||||
@@ -577,7 +577,6 @@ impl Window {
|
|||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
WindowsApi::raise_and_focus_window(self.hwnd)?;
|
WindowsApi::raise_and_focus_window(self.hwnd)?;
|
||||||
|
|
||||||
@@ -593,7 +592,7 @@ impl Window {
|
|||||||
WindowsApi::foreground_window().unwrap_or_default() == self.hwnd
|
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 animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
|
||||||
let transparent_enabled = animation_enabled.get(&TransparencyRenderDispatcher::PREFIX);
|
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 animation_enabled = ANIMATION_ENABLED_PER_ANIMATION.lock();
|
||||||
let transparent_enabled = animation_enabled.get(&TransparencyRenderDispatcher::PREFIX);
|
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))
|
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)
|
WindowsApi::set_window_accent(self.hwnd, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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())?)
|
WindowsApi::update_style(self.hwnd, isize::try_from(style.bits())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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())?)
|
WindowsApi::update_style(self.hwnd, i32::try_from(style.bits())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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())?)
|
WindowsApi::update_ex_style(self.hwnd, isize::try_from(style.bits())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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())?)
|
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)?)?;
|
let bits = u32::try_from(WindowsApi::gwl_style(self.hwnd)?)?;
|
||||||
Ok(WindowStyle::from_bits_truncate(bits))
|
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)?)?;
|
let bits = u32::try_from(WindowsApi::gwl_ex_style(self.hwnd)?)?;
|
||||||
Ok(ExtendedWindowStyle::from_bits_truncate(bits))
|
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)
|
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 (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd);
|
||||||
let handle = WindowsApi::process_handle(process_id)?;
|
let handle = WindowsApi::process_handle(process_id)?;
|
||||||
let path = WindowsApi::exe_path(handle);
|
let path = WindowsApi::exe_path(handle);
|
||||||
@@ -716,7 +715,7 @@ impl Window {
|
|||||||
path
|
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 (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd);
|
||||||
let handle = WindowsApi::process_handle(process_id)?;
|
let handle = WindowsApi::process_handle(process_id)?;
|
||||||
let exe = WindowsApi::exe(handle);
|
let exe = WindowsApi::exe(handle);
|
||||||
@@ -729,11 +728,11 @@ impl Window {
|
|||||||
process_id
|
process_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn class(self) -> Result<String> {
|
pub fn class(self) -> eyre::Result<String> {
|
||||||
WindowsApi::real_window_class_w(self.hwnd)
|
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)
|
WindowsApi::is_window_cloaked(self.hwnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,14 +740,14 @@ impl Window {
|
|||||||
WindowsApi::is_window(self.hwnd)
|
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()?;
|
let mut style = self.style()?;
|
||||||
style.remove(WindowStyle::CAPTION);
|
style.remove(WindowStyle::CAPTION);
|
||||||
style.remove(WindowStyle::THICKFRAME);
|
style.remove(WindowStyle::THICKFRAME);
|
||||||
self.update_style(&style)
|
self.update_style(&style)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_title_bar(self) -> Result<()> {
|
pub fn add_title_bar(self) -> eyre::Result<()> {
|
||||||
let mut style = self.style()?;
|
let mut style = self.style()?;
|
||||||
style.insert(WindowStyle::CAPTION);
|
style.insert(WindowStyle::CAPTION);
|
||||||
style.insert(WindowStyle::THICKFRAME);
|
style.insert(WindowStyle::THICKFRAME);
|
||||||
@@ -759,7 +758,7 @@ impl Window {
|
|||||||
/// it. Use raise_and_focus_window to activate and focus a 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 also checks if there is a border attached to this window and if it is
|
||||||
/// it raises it as well.
|
/// it raises it as well.
|
||||||
pub fn raise(self) -> Result<()> {
|
pub fn raise(self) -> eyre::Result<()> {
|
||||||
WindowsApi::raise_window(self.hwnd)?;
|
WindowsApi::raise_window(self.hwnd)?;
|
||||||
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
|
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
|
||||||
WindowsApi::raise_window(border_info.border_hwnd)?;
|
WindowsApi::raise_window(border_info.border_hwnd)?;
|
||||||
@@ -771,7 +770,7 @@ impl Window {
|
|||||||
/// it.
|
/// it.
|
||||||
/// It also checks if there is a border attached to this window and if it is
|
/// It also checks if there is a border attached to this window and if it is
|
||||||
/// it lowers it as well.
|
/// it lowers it as well.
|
||||||
pub fn lower(self) -> Result<()> {
|
pub fn lower(self) -> eyre::Result<()> {
|
||||||
WindowsApi::lower_window(self.hwnd)?;
|
WindowsApi::lower_window(self.hwnd)?;
|
||||||
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
|
if let Some(border_info) = crate::border_manager::window_border(self.hwnd) {
|
||||||
WindowsApi::lower_window(border_info.border_hwnd)?;
|
WindowsApi::lower_window(border_info.border_hwnd)?;
|
||||||
@@ -784,7 +783,7 @@ impl Window {
|
|||||||
self,
|
self,
|
||||||
event: Option<WindowManagerEvent>,
|
event: Option<WindowManagerEvent>,
|
||||||
debug: &mut RuleDebug,
|
debug: &mut RuleDebug,
|
||||||
) -> Result<bool> {
|
) -> eyre::Result<bool> {
|
||||||
if !self.is_window() {
|
if !self.is_window() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -817,14 +816,14 @@ impl Window {
|
|||||||
|
|
||||||
let mut allow_cloaked = false;
|
let mut allow_cloaked = false;
|
||||||
|
|
||||||
if let Some(event) = event {
|
if let Some(event) = event
|
||||||
if matches!(
|
&& matches!(
|
||||||
event,
|
event,
|
||||||
WindowManagerEvent::Hide(_, _) | WindowManagerEvent::Cloak(_, _)
|
WindowManagerEvent::Hide(_, _) | WindowManagerEvent::Cloak(_, _)
|
||||||
) {
|
)
|
||||||
|
{
|
||||||
allow_cloaked = true;
|
allow_cloaked = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
debug.allow_cloaked = allow_cloaked;
|
debug.allow_cloaked = allow_cloaked;
|
||||||
|
|
||||||
@@ -1302,33 +1301,33 @@ pub fn should_act_individual(
|
|||||||
},
|
},
|
||||||
Some(MatchingStrategy::Regex) => match identifier.kind {
|
Some(MatchingStrategy::Regex) => match identifier.kind {
|
||||||
ApplicationIdentifier::Title => {
|
ApplicationIdentifier::Title => {
|
||||||
if let Some(re) = regex_identifiers.get(&identifier.id) {
|
if let Some(re) = regex_identifiers.get(&identifier.id)
|
||||||
if re.is_match(title) {
|
&& re.is_match(title)
|
||||||
|
{
|
||||||
should_act = true;
|
should_act = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ApplicationIdentifier::Class => {
|
ApplicationIdentifier::Class => {
|
||||||
if let Some(re) = regex_identifiers.get(&identifier.id) {
|
if let Some(re) = regex_identifiers.get(&identifier.id)
|
||||||
if re.is_match(class) {
|
&& re.is_match(class)
|
||||||
|
{
|
||||||
should_act = true;
|
should_act = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ApplicationIdentifier::Exe => {
|
ApplicationIdentifier::Exe => {
|
||||||
if let Some(re) = regex_identifiers.get(&identifier.id) {
|
if let Some(re) = regex_identifiers.get(&identifier.id)
|
||||||
if re.is_match(exe_name) {
|
&& re.is_match(exe_name)
|
||||||
|
{
|
||||||
should_act = true;
|
should_act = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ApplicationIdentifier::Path => {
|
ApplicationIdentifier::Path => {
|
||||||
if let Some(re) = regex_identifiers.get(&identifier.id) {
|
if let Some(re) = regex_identifiers.get(&identifier.id)
|
||||||
if re.is_match(path) {
|
&& re.is_match(path)
|
||||||
|
{
|
||||||
should_act = true;
|
should_act = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+735
-759
File diff suppressed because it is too large
Load Diff
@@ -4,12 +4,12 @@ use std::fmt::Formatter;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
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_ON_LAUNCH;
|
||||||
use crate::OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST;
|
use crate::OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST;
|
||||||
use crate::REGEX_IDENTIFIERS;
|
use crate::REGEX_IDENTIFIERS;
|
||||||
|
use crate::window::Window;
|
||||||
|
use crate::window::should_act;
|
||||||
|
use crate::winevent::WinEvent;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
|
|||||||
+164
-163
@@ -1,18 +1,15 @@
|
|||||||
use color_eyre::eyre::anyhow;
|
use color_eyre::eyre;
|
||||||
use color_eyre::eyre::bail;
|
|
||||||
use color_eyre::eyre::Error;
|
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 core::ffi::c_void;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
use std::path::Path;
|
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::COLORREF;
|
||||||
|
use windows::Win32::Foundation::CloseHandle;
|
||||||
use windows::Win32::Foundation::HANDLE;
|
use windows::Win32::Foundation::HANDLE;
|
||||||
use windows::Win32::Foundation::HINSTANCE;
|
use windows::Win32::Foundation::HINSTANCE;
|
||||||
use windows::Win32::Foundation::HMODULE;
|
use windows::Win32::Foundation::HMODULE;
|
||||||
@@ -21,8 +18,9 @@ use windows::Win32::Foundation::LPARAM;
|
|||||||
use windows::Win32::Foundation::POINT;
|
use windows::Win32::Foundation::POINT;
|
||||||
use windows::Win32::Foundation::RECT;
|
use windows::Win32::Foundation::RECT;
|
||||||
use windows::Win32::Foundation::WPARAM;
|
use windows::Win32::Foundation::WPARAM;
|
||||||
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
|
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
||||||
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
|
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_BORDER_COLOR;
|
||||||
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
|
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
|
||||||
use windows::Win32::Graphics::Dwm::DWMWA_COLOR_NONE;
|
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::DWMWA_WINDOW_CORNER_PREFERENCE;
|
||||||
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
|
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
|
||||||
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
|
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
|
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
|
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
|
||||||
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
|
|
||||||
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
|
||||||
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
|
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
|
||||||
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
|
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::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::MonitorFromPoint;
|
||||||
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
|
||||||
use windows::Win32::Graphics::Gdi::Rectangle;
|
use windows::Win32::Graphics::Gdi::Rectangle;
|
||||||
use windows::Win32::Graphics::Gdi::RoundRect;
|
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::CLSCTX_ALL;
|
||||||
|
use windows::Win32::System::Com::CoCreateInstance;
|
||||||
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
|
||||||
use windows::Win32::System::Power::RegisterPowerSettingNotification;
|
|
||||||
use windows::Win32::System::Power::HPOWERNOTIFY;
|
use windows::Win32::System::Power::HPOWERNOTIFY;
|
||||||
|
use windows::Win32::System::Power::RegisterPowerSettingNotification;
|
||||||
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
|
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
|
||||||
use windows::Win32::System::RemoteDesktop::WTSRegisterSessionNotification;
|
use windows::Win32::System::RemoteDesktop::WTSRegisterSessionNotification;
|
||||||
use windows::Win32::System::Threading::GetCurrentProcessId;
|
use windows::Win32::System::Threading::GetCurrentProcessId;
|
||||||
use windows::Win32::System::Threading::OpenProcess;
|
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_ACCESS_RIGHTS;
|
||||||
use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
|
use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
|
||||||
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
||||||
use windows::Win32::UI::HiDpi::GetDpiForMonitor;
|
use windows::Win32::System::Threading::QueryFullProcessImageNameW;
|
||||||
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
|
||||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
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::MDT_EFFECTIVE_DPI;
|
||||||
|
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::GetKeyState;
|
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;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0;
|
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
|
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
|
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_LBUTTON;
|
||||||
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
|
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::DesktopWallpaper;
|
||||||
use windows::Win32::UI::Shell::IDesktopWallpaper;
|
use windows::Win32::UI::Shell::IDesktopWallpaper;
|
||||||
use windows::Win32::UI::Shell::DWPOS_FILL;
|
|
||||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW;
|
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::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::GetCursorPos;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
|
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
|
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::GetWindowRect;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowTextW;
|
use windows::Win32::UI::WindowsAndMessaging::GetWindowTextW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
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::IsIconic;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::IsZoomed;
|
use windows::Win32::UI::WindowsAndMessaging::IsZoomed;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::LWA_ALPHA;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
|
use windows::Win32::UI::WindowsAndMessaging::MoveWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
|
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::RealGetWindowClassW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
use windows::Win32::UI::WindowsAndMessaging::RegisterClassW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::RegisterDeviceNotificationW;
|
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::SetCursorPos;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes;
|
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::ShowWindow;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::ShowWindowAsync;
|
use windows::Win32::UI::WindowsAndMessaging::ShowWindowAsync;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
|
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::WINDOW_LONG_PTR_INDEX;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
|
use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW;
|
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_EX_TOPMOST;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
|
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::BOOL;
|
||||||
use windows_core::HSTRING;
|
use windows_core::HSTRING;
|
||||||
|
|
||||||
use crate::core::Rect;
|
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::container::Container;
|
||||||
use crate::monitor;
|
use crate::monitor;
|
||||||
use crate::monitor::Monitor;
|
use crate::monitor::Monitor;
|
||||||
use crate::ring::Ring;
|
use crate::ring::Ring;
|
||||||
use crate::set_window_position::SetWindowPosition;
|
use crate::set_window_position::SetWindowPosition;
|
||||||
use crate::windows_callbacks;
|
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 {
|
macro_rules! as_ptr {
|
||||||
($value:expr) => {
|
($value:expr) => {
|
||||||
@@ -207,7 +207,7 @@ impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait ProcessWindowsCrateResult<T> {
|
pub trait ProcessWindowsCrateResult<T> {
|
||||||
fn process(self) -> Result<T>;
|
fn process(self) -> eyre::Result<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_process_windows_crate_integer_wrapper_result {
|
macro_rules! impl_process_windows_crate_integer_wrapper_result {
|
||||||
@@ -215,7 +215,7 @@ macro_rules! impl_process_windows_crate_integer_wrapper_result {
|
|||||||
paste::paste! {
|
paste::paste! {
|
||||||
$(
|
$(
|
||||||
impl ProcessWindowsCrateResult<$deref> for $input {
|
impl ProcessWindowsCrateResult<$deref> for $input {
|
||||||
fn process(self) -> Result<$deref> {
|
fn process(self) -> eyre::Result<$deref> {
|
||||||
if self == $input(std::ptr::null_mut()) {
|
if self == $input(std::ptr::null_mut()) {
|
||||||
Err(std::io::Error::last_os_error().into())
|
Err(std::io::Error::last_os_error().into())
|
||||||
} else {
|
} else {
|
||||||
@@ -233,7 +233,7 @@ impl_process_windows_crate_integer_wrapper_result!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl<T> ProcessWindowsCrateResult<T> for WindowsCrateResult<T> {
|
impl<T> ProcessWindowsCrateResult<T> for WindowsCrateResult<T> {
|
||||||
fn process(self) -> Result<T> {
|
fn process(self) -> eyre::Result<T> {
|
||||||
match self {
|
match self {
|
||||||
Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
Err(error) => Err(error.into()),
|
Err(error) => Err(error.into()),
|
||||||
@@ -247,13 +247,13 @@ impl WindowsApi {
|
|||||||
pub fn enum_display_monitors(
|
pub fn enum_display_monitors(
|
||||||
callback: MONITORENUMPROC,
|
callback: MONITORENUMPROC,
|
||||||
callback_data_address: isize,
|
callback_data_address: isize,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
unsafe { EnumDisplayMonitors(None, None, callback, LPARAM(callback_data_address)) }
|
unsafe { EnumDisplayMonitors(None, None, callback, LPARAM(callback_data_address)) }
|
||||||
.ok()
|
.ok()
|
||||||
.process()
|
.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()
|
Ok(win32_display_data::connected_displays_all()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|d| {
|
.map(|d| {
|
||||||
@@ -265,7 +265,7 @@ impl WindowsApi {
|
|||||||
.collect::<Vec<_>>())
|
.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 monitors = &mut wm.monitors;
|
||||||
let monitor_usr_idx_map = &mut wm.monitor_usr_idx_map;
|
let monitor_usr_idx_map = &mut wm.monitor_usr_idx_map;
|
||||||
|
|
||||||
@@ -282,15 +282,15 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for d in &all_displays {
|
for d in &all_displays {
|
||||||
if let Some(id) = &d.serial_number_id {
|
if let Some(id) = &d.serial_number_id
|
||||||
if serial_id_map.get(id).copied().unwrap_or_default() > 1 {
|
&& serial_id_map.get(id).copied().unwrap_or_default() > 1
|
||||||
|
{
|
||||||
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
|
let mut dupes = DUPLICATE_MONITOR_SERIAL_IDS.write();
|
||||||
if !dupes.contains(id) {
|
if !dupes.contains(id) {
|
||||||
(*dupes).push(id.clone());
|
(*dupes).push(id.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
'read: for mut display in all_displays {
|
'read: for mut display in all_displays {
|
||||||
let path = display.device_path.clone();
|
let path = display.device_path.clone();
|
||||||
@@ -310,7 +310,7 @@ impl WindowsApi {
|
|||||||
let name = name.split('\\').collect::<Vec<_>>()[0].to_string();
|
let name = name.split('\\').collect::<Vec<_>>()[0].to_string();
|
||||||
|
|
||||||
for monitor in monitors.elements() {
|
for monitor in monitors.elements() {
|
||||||
if device_id.eq(monitor.device_id()) {
|
if device_id.eq(&monitor.device_id) {
|
||||||
continue 'read;
|
continue 'read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,15 +335,14 @@ impl WindowsApi {
|
|||||||
let mut index_preference = None;
|
let mut index_preference = None;
|
||||||
let monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
|
let monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
|
||||||
for (index, monitor_size) in &*monitor_index_preferences {
|
for (index, monitor_size) in &*monitor_index_preferences {
|
||||||
if m.size() == monitor_size {
|
if m.size == *monitor_size {
|
||||||
index_preference = Option::from(index);
|
index_preference = Option::from(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
let display_index_preferences = DISPLAY_INDEX_PREFERENCES.read();
|
||||||
for (index, id) in &*display_index_preferences {
|
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);
|
index_preference = Option::from(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +355,7 @@ impl WindowsApi {
|
|||||||
let current_name = monitors
|
let current_name = monitors
|
||||||
.elements_mut()
|
.elements_mut()
|
||||||
.get(*preference)
|
.get(*preference)
|
||||||
.map_or("", |m| m.name());
|
.map_or("", |m| &m.name);
|
||||||
if current_name == "PLACEHOLDER" {
|
if current_name == "PLACEHOLDER" {
|
||||||
let _ = monitors.elements_mut().remove(*preference);
|
let _ = monitors.elements_mut().remove(*preference);
|
||||||
monitors.elements_mut().insert(*preference, m);
|
monitors.elements_mut().insert(*preference, m);
|
||||||
@@ -368,16 +367,14 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
monitors
|
monitors.elements_mut().retain(|m| m.name.ne("PLACEHOLDER"));
|
||||||
.elements_mut()
|
|
||||||
.retain(|m| m.name().ne("PLACEHOLDER"));
|
|
||||||
|
|
||||||
// Rebuild monitor index map
|
// Rebuild monitor index map
|
||||||
*monitor_usr_idx_map = HashMap::new();
|
*monitor_usr_idx_map = HashMap::new();
|
||||||
let mut added_monitor_idxs = Vec::new();
|
let mut added_monitor_idxs = Vec::new();
|
||||||
for (index, id) in &*DISPLAY_INDEX_PREFERENCES.read() {
|
for (index, id) in &*DISPLAY_INDEX_PREFERENCES.read() {
|
||||||
if let Some(m_idx) = monitors.elements().iter().position(|m| {
|
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);
|
monitor_usr_idx_map.insert(*index, m_idx);
|
||||||
added_monitor_idxs.push(m_idx);
|
added_monitor_idxs.push(m_idx);
|
||||||
@@ -409,13 +406,13 @@ impl WindowsApi {
|
|||||||
Ok(())
|
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()
|
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() {
|
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() {
|
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
|
||||||
// EnumWindows will enumerate through windows on all monitors
|
// EnumWindows will enumerate through windows on all monitors
|
||||||
Self::enum_windows(
|
Self::enum_windows(
|
||||||
@@ -426,7 +423,7 @@ impl WindowsApi {
|
|||||||
// Ensure that the resize_dimensions Vec length matches the number of containers for
|
// 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
|
// the potential later calls to workspace.remove_window later in this fn
|
||||||
let len = workspace.containers().len();
|
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
|
// We have to prune each monitor's primary workspace of undesired windows here
|
||||||
let mut windows_on_other_monitors = vec![];
|
let mut windows_on_other_monitors = vec![];
|
||||||
@@ -448,7 +445,7 @@ impl WindowsApi {
|
|||||||
Ok(())
|
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()
|
unsafe { AllowSetForegroundWindow(process_id) }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,13 +455,13 @@ impl WindowsApi {
|
|||||||
unsafe { MonitorFromWindow(HWND(as_ptr!(hwnd)), MONITOR_DEFAULTTONEAREST) }.0 as isize
|
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
|
// MONITOR_DEFAULTTONEAREST ensures that the return value will never be NULL
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
|
||||||
Ok(Self::monitor(
|
Ok(Self::monitor(
|
||||||
unsafe { MonitorFromWindow(HWND(as_ptr!(hwnd)), MONITOR_DEFAULTTONEAREST) }.0 as isize,
|
unsafe { MonitorFromWindow(HWND(as_ptr!(hwnd)), MONITOR_DEFAULTTONEAREST) }.0 as isize,
|
||||||
)?
|
)?
|
||||||
.name()
|
.name
|
||||||
.to_string())
|
.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +479,7 @@ impl WindowsApi {
|
|||||||
layout: &Rect,
|
layout: &Rect,
|
||||||
top: bool,
|
top: bool,
|
||||||
with_async_window_pos: bool,
|
with_async_window_pos: bool,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
let hwnd = HWND(as_ptr!(hwnd));
|
let hwnd = HWND(as_ptr!(hwnd));
|
||||||
|
|
||||||
let mut flags = SetWindowPosition::NO_ACTIVATE
|
let mut flags = SetWindowPosition::NO_ACTIVATE
|
||||||
@@ -533,13 +530,13 @@ impl WindowsApi {
|
|||||||
Self::set_window_pos(hwnd, &rect, HWND_TOP, flags.bits())
|
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()
|
unsafe { BringWindowToTop(HWND(as_ptr!(hwnd))) }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raise the window to the top of the Z order, but do not activate or focus
|
/// 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.
|
/// 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
|
let mut flags = SetWindowPosition::NO_MOVE
|
||||||
| SetWindowPosition::NO_SIZE
|
| SetWindowPosition::NO_SIZE
|
||||||
| SetWindowPosition::NO_ACTIVATE
|
| 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
|
/// Lower the window to the bottom of the Z order, but do not activate or focus
|
||||||
/// it.
|
/// it.
|
||||||
pub fn lower_window(hwnd: isize) -> Result<()> {
|
pub fn lower_window(hwnd: isize) -> eyre::Result<()> {
|
||||||
let mut flags = SetWindowPosition::NO_MOVE
|
let mut flags = SetWindowPosition::NO_MOVE
|
||||||
| SetWindowPosition::NO_SIZE
|
| SetWindowPosition::NO_SIZE
|
||||||
| SetWindowPosition::NO_ACTIVATE
|
| 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
|
let mut flags = SetWindowPosition::NO_SEND_CHANGING
|
||||||
| SetWindowPosition::NO_ACTIVATE
|
| SetWindowPosition::NO_ACTIVATE
|
||||||
| SetWindowPosition::NO_REDRAW
|
| SetWindowPosition::NO_REDRAW
|
||||||
@@ -607,7 +604,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// set_window_pos calls SetWindowPos without any accounting for Window decorations.
|
/// 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 {
|
unsafe {
|
||||||
SetWindowPos(
|
SetWindowPos(
|
||||||
hwnd,
|
hwnd,
|
||||||
@@ -623,7 +620,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// move_windows calls MoveWindow, but cannot be called with async window pos, so it might hang
|
/// 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 hwnd = HWND(as_ptr!(hwnd));
|
||||||
|
|
||||||
let shadow_rect = Self::shadow_rect(hwnd).unwrap_or_default();
|
let shadow_rect = Self::shadow_rect(hwnd).unwrap_or_default();
|
||||||
@@ -658,15 +655,16 @@ impl WindowsApi {
|
|||||||
Self::show_window(hwnd, SW_MINIMIZE);
|
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()
|
unsafe { PostMessageW(Option::from(hwnd), message, wparam, lparam) }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close_window(hwnd: isize) -> Result<()> {
|
pub fn close_window(hwnd: isize) -> eyre::Result<()> {
|
||||||
match Self::post_message(HWND(as_ptr!(hwnd)), WM_CLOSE, WPARAM(0), LPARAM(0)) {
|
if Self::post_message(HWND(as_ptr!(hwnd)), WM_CLOSE, WPARAM(0), LPARAM(0)).is_err() {
|
||||||
Ok(()) => Ok(()),
|
bail!("could not close window");
|
||||||
Err(_) => Err(anyhow!("could not close window")),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_window(hwnd: isize) {
|
pub fn hide_window(hwnd: isize) {
|
||||||
@@ -685,11 +683,11 @@ impl WindowsApi {
|
|||||||
Self::show_window(hwnd, SW_MAXIMIZE);
|
Self::show_window(hwnd, SW_MAXIMIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn foreground_window() -> Result<isize> {
|
pub fn foreground_window() -> eyre::Result<isize> {
|
||||||
unsafe { GetForegroundWindow() }.process()
|
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 {
|
let event = [INPUT {
|
||||||
r#type: INPUT_MOUSE,
|
r#type: INPUT_MOUSE,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -717,20 +715,20 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn top_window() -> Result<isize> {
|
pub fn top_window() -> eyre::Result<isize> {
|
||||||
unsafe { GetTopWindow(None)? }.process()
|
unsafe { GetTopWindow(None)? }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn desktop_window() -> Result<isize> {
|
pub fn desktop_window() -> eyre::Result<isize> {
|
||||||
unsafe { GetDesktopWindow() }.process()
|
unsafe { GetDesktopWindow() }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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()
|
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![];
|
let mut hwnds = vec![];
|
||||||
Self::enum_windows(
|
Self::enum_windows(
|
||||||
Some(windows_callbacks::alt_tab_windows),
|
Some(windows_callbacks::alt_tab_windows),
|
||||||
@@ -741,7 +739,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn top_visible_window() -> Result<isize> {
|
pub fn top_visible_window() -> eyre::Result<isize> {
|
||||||
let hwnd = Self::top_window()?;
|
let hwnd = Self::top_window()?;
|
||||||
let mut next_hwnd = hwnd;
|
let mut next_hwnd = hwnd;
|
||||||
|
|
||||||
@@ -753,10 +751,10 @@ impl WindowsApi {
|
|||||||
next_hwnd = Self::next_window(next_hwnd)?;
|
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() };
|
let mut rect = unsafe { std::mem::zeroed() };
|
||||||
|
|
||||||
if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_ok() {
|
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
|
/// 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
|
/// added to a position rect to compute a size for set_window_pos that will
|
||||||
/// fill the target area, ignoring shadows.
|
/// 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 window_rect = Self::window_rect(hwnd.0 as isize)?;
|
||||||
|
|
||||||
let mut srect = Default::default();
|
let mut srect = Default::default();
|
||||||
@@ -810,26 +808,26 @@ impl WindowsApi {
|
|||||||
let _ = Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
|
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()
|
unsafe { SetCursorPos(x, y) }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor_pos() -> Result<POINT> {
|
pub fn cursor_pos() -> eyre::Result<POINT> {
|
||||||
let mut cursor_pos = POINT::default();
|
let mut cursor_pos = POINT::default();
|
||||||
unsafe { GetCursorPos(&mut cursor_pos) }.process()?;
|
unsafe { GetCursorPos(&mut cursor_pos) }.process()?;
|
||||||
|
|
||||||
Ok(cursor_pos)
|
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()
|
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()?)
|
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))
|
Self::set_cursor_pos(rect.left + (rect.right / 2), rect.top + (rect.bottom / 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -852,17 +850,17 @@ impl WindowsApi {
|
|||||||
unsafe { GetCurrentProcessId() }
|
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 process_id = Self::current_process_id();
|
||||||
let mut session_id = 0;
|
let mut session_id = 0;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if ProcessIdToSessionId(process_id, &mut session_id).is_ok() {
|
if ProcessIdToSessionId(process_id, &mut session_id).is_err() {
|
||||||
|
bail!("could not determine current session id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(session_id)
|
Ok(session_id)
|
||||||
} else {
|
|
||||||
Err(anyhow!("could not determine current session id"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
@@ -870,7 +868,7 @@ impl WindowsApi {
|
|||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
index: WINDOW_LONG_PTR_INDEX,
|
index: WINDOW_LONG_PTR_INDEX,
|
||||||
new_value: isize,
|
new_value: isize,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
Result::from(WindowsResult::from(unsafe {
|
Result::from(WindowsResult::from(unsafe {
|
||||||
SetWindowLongPtrW(hwnd, index, new_value)
|
SetWindowLongPtrW(hwnd, index, new_value)
|
||||||
}))
|
}))
|
||||||
@@ -882,7 +880,7 @@ impl WindowsApi {
|
|||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
index: WINDOW_LONG_PTR_INDEX,
|
index: WINDOW_LONG_PTR_INDEX,
|
||||||
new_value: i32,
|
new_value: i32,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
Result::from(WindowsResult::from(unsafe {
|
Result::from(WindowsResult::from(unsafe {
|
||||||
SetWindowLongPtrW(hwnd, index, new_value)
|
SetWindowLongPtrW(hwnd, index, new_value)
|
||||||
}))
|
}))
|
||||||
@@ -890,27 +888,27 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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)
|
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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)
|
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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)
|
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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)
|
Self::window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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
|
// 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
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
|
||||||
Result::from(WindowsResult::from(unsafe {
|
Result::from(WindowsResult::from(unsafe {
|
||||||
@@ -919,7 +917,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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
|
// 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
|
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
|
||||||
Result::from(WindowsResult::from(unsafe {
|
Result::from(WindowsResult::from(unsafe {
|
||||||
@@ -928,26 +926,26 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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)
|
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE, new_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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)
|
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_STYLE, new_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[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)
|
Self::set_window_long_ptr_w(HWND(as_ptr!(hwnd)), GWL_EXSTYLE, new_value)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[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)
|
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];
|
let mut text: [u16; 512] = [0; 512];
|
||||||
match WindowsResult::from(unsafe { GetWindowTextW(HWND(as_ptr!(hwnd)), &mut text) }) {
|
match WindowsResult::from(unsafe { GetWindowTextW(HWND(as_ptr!(hwnd)), &mut text) }) {
|
||||||
WindowsResult::Ok(len) => {
|
WindowsResult::Ok(len) => {
|
||||||
@@ -962,19 +960,19 @@ impl WindowsApi {
|
|||||||
access_rights: PROCESS_ACCESS_RIGHTS,
|
access_rights: PROCESS_ACCESS_RIGHTS,
|
||||||
inherit_handle: bool,
|
inherit_handle: bool,
|
||||||
process_id: u32,
|
process_id: u32,
|
||||||
) -> Result<HANDLE> {
|
) -> eyre::Result<HANDLE> {
|
||||||
unsafe { OpenProcess(access_rights, inherit_handle, process_id) }.process()
|
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()
|
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)
|
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 len = 260_u32;
|
||||||
let mut path: Vec<u16> = vec![0; len as usize];
|
let mut path: Vec<u16> = vec![0; len as usize];
|
||||||
let text_ptr = path.as_mut_ptr();
|
let text_ptr = path.as_mut_ptr();
|
||||||
@@ -987,15 +985,15 @@ impl WindowsApi {
|
|||||||
Ok(String::from_utf16(&path[..len as usize])?)
|
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)?
|
Ok(Self::exe_path(handle)?
|
||||||
.split('\\')
|
.split('\\')
|
||||||
.next_back()
|
.next_back()
|
||||||
.ok_or_else(|| anyhow!("there is no last element"))?
|
.ok_or_eyre("there is no last element")?
|
||||||
.to_string())
|
.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;
|
const BUF_SIZE: usize = 512;
|
||||||
let mut class: [u16; BUF_SIZE] = [0; BUF_SIZE];
|
let mut class: [u16; BUF_SIZE] = [0; BUF_SIZE];
|
||||||
|
|
||||||
@@ -1010,7 +1008,7 @@ impl WindowsApi {
|
|||||||
hwnd: isize,
|
hwnd: isize,
|
||||||
attribute: DWMWINDOWATTRIBUTE,
|
attribute: DWMWINDOWATTRIBUTE,
|
||||||
value: &mut T,
|
value: &mut T,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
DwmGetWindowAttribute(
|
DwmGetWindowAttribute(
|
||||||
HWND(as_ptr!(hwnd)),
|
HWND(as_ptr!(hwnd)),
|
||||||
@@ -1023,7 +1021,7 @@ impl WindowsApi {
|
|||||||
Ok(())
|
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;
|
let mut cloaked: u32 = 0;
|
||||||
Self::dwm_get_window_attribute(hwnd, DWMWA_CLOAKED, &mut cloaked)?;
|
Self::dwm_get_window_attribute(hwnd, DWMWA_CLOAKED, &mut cloaked)?;
|
||||||
|
|
||||||
@@ -1049,7 +1047,7 @@ impl WindowsApi {
|
|||||||
unsafe { IsZoomed(HWND(as_ptr!(hwnd))) }.into()
|
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();
|
let mut ex_info = MONITORINFOEXW::default();
|
||||||
ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;
|
ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;
|
||||||
unsafe { GetMonitorInfoW(hmonitor, &mut ex_info.monitorInfo) }
|
unsafe { GetMonitorInfoW(hmonitor, &mut ex_info.monitorInfo) }
|
||||||
@@ -1069,7 +1067,7 @@ impl WindowsApi {
|
|||||||
None
|
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() {
|
for mut display in win32_display_data::connected_displays_all().flatten() {
|
||||||
if display.hmonitor == hmonitor {
|
if display.hmonitor == hmonitor {
|
||||||
let path = display.device_path;
|
let path = display.device_path;
|
||||||
@@ -1112,7 +1110,7 @@ impl WindowsApi {
|
|||||||
bail!("could not find device_id for hmonitor: {hmonitor}");
|
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) }
|
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }
|
||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
@@ -1123,13 +1121,13 @@ impl WindowsApi {
|
|||||||
ui_param: u32,
|
ui_param: u32,
|
||||||
pv_param: *mut c_void,
|
pv_param: *mut c_void,
|
||||||
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
|
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
|
||||||
) -> Result<()> {
|
) -> eyre::Result<()> {
|
||||||
unsafe { SystemParametersInfoW(action, ui_param, Option::from(pv_param), update_flags) }
|
unsafe { SystemParametersInfoW(action, ui_param, Option::from(pv_param), update_flags) }
|
||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub fn foreground_lock_timeout() -> Result<()> {
|
pub fn foreground_lock_timeout() -> eyre::Result<()> {
|
||||||
let mut value: u32 = 0;
|
let mut value: u32 = 0;
|
||||||
|
|
||||||
Self::system_parameters_info_w(
|
Self::system_parameters_info_w(
|
||||||
@@ -1142,7 +1140,9 @@ impl WindowsApi {
|
|||||||
tracing::info!("current value of ForegroundLockTimeout is {value}");
|
tracing::info!("current value of ForegroundLockTimeout is {value}");
|
||||||
|
|
||||||
if value != 0 {
|
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(
|
Self::system_parameters_info_w(
|
||||||
SPI_SETFOREGROUNDLOCKTIMEOUT,
|
SPI_SETFOREGROUNDLOCKTIMEOUT,
|
||||||
@@ -1165,7 +1165,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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() };
|
let mut is_enabled: BOOL = unsafe { std::mem::zeroed() };
|
||||||
|
|
||||||
Self::system_parameters_info_w(
|
Self::system_parameters_info_w(
|
||||||
@@ -1179,7 +1179,8 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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(
|
Self::system_parameters_info_w(
|
||||||
SPI_SETACTIVEWINDOWTRACKING,
|
SPI_SETACTIVEWINDOWTRACKING,
|
||||||
0,
|
0,
|
||||||
@@ -1189,7 +1190,7 @@ impl WindowsApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn disable_focus_follows_mouse() -> Result<()> {
|
pub fn disable_focus_follows_mouse() -> eyre::Result<()> {
|
||||||
Self::system_parameters_info_w(
|
Self::system_parameters_info_w(
|
||||||
SPI_SETACTIVEWINDOWTRACKING,
|
SPI_SETACTIVEWINDOWTRACKING,
|
||||||
0,
|
0,
|
||||||
@@ -1198,7 +1199,7 @@ impl WindowsApi {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn module_handle_w() -> Result<HMODULE> {
|
pub fn module_handle_w() -> eyre::Result<HMODULE> {
|
||||||
unsafe { GetModuleHandleW(None) }.process()
|
unsafe { GetModuleHandleW(None) }.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,11 +1207,11 @@ impl WindowsApi {
|
|||||||
unsafe { CreateSolidBrush(COLORREF(colour)) }
|
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) }))
|
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_x = u32::default();
|
||||||
let mut dpi_y = u32::default();
|
let mut dpi_y = u32::default();
|
||||||
|
|
||||||
@@ -1228,14 +1229,14 @@ impl WindowsApi {
|
|||||||
Ok(dpi_y as f32 / 96.0)
|
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_a = Self::dpi_for_monitor(hmonitor_a)?;
|
||||||
let dpi_b = Self::dpi_for_monitor(hmonitor_b)?;
|
let dpi_b = Self::dpi_for_monitor(hmonitor_b)?;
|
||||||
|
|
||||||
Ok((dpi_a - dpi_b).abs() < f32::EPSILON)
|
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;
|
let round = DWMWCP_ROUND;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -1249,7 +1250,7 @@ impl WindowsApi {
|
|||||||
.process()
|
.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));
|
let col_ref = COLORREF(color.unwrap_or(DWMWA_COLOR_NONE));
|
||||||
unsafe {
|
unsafe {
|
||||||
DwmSetWindowAttribute(
|
DwmSetWindowAttribute(
|
||||||
@@ -1266,7 +1267,7 @@ impl WindowsApi {
|
|||||||
name: PCWSTR,
|
name: PCWSTR,
|
||||||
instance: isize,
|
instance: isize,
|
||||||
border: *mut Border,
|
border: *mut Border,
|
||||||
) -> Result<isize> {
|
) -> eyre::Result<isize> {
|
||||||
unsafe {
|
unsafe {
|
||||||
CreateWindowExW(
|
CreateWindowExW(
|
||||||
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
|
WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE,
|
||||||
@@ -1286,7 +1287,7 @@ impl WindowsApi {
|
|||||||
.process()
|
.process()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_transparent(hwnd: isize, alpha: u8) -> Result<()> {
|
pub fn set_transparent(hwnd: isize, alpha: u8) -> eyre::Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
SetLayeredWindowAttributes(
|
SetLayeredWindowAttributes(
|
||||||
@@ -1300,7 +1301,7 @@ impl WindowsApi {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_transparent(hwnd: isize) -> Result<u8> {
|
pub fn get_transparent(hwnd: isize) -> eyre::Result<u8> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut alpha: u8 = u8::default();
|
let mut alpha: u8 = u8::default();
|
||||||
let mut color_ref = COLORREF(-1i32 as u32);
|
let mut color_ref = COLORREF(-1i32 as u32);
|
||||||
@@ -1315,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 {
|
unsafe {
|
||||||
CreateWindowExW(
|
CreateWindowExW(
|
||||||
WS_EX_NOACTIVATE,
|
WS_EX_NOACTIVATE,
|
||||||
@@ -1409,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()
|
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 path = path.canonicalize()?;
|
||||||
|
|
||||||
let wallpaper: IDesktopWallpaper =
|
let wallpaper: IDesktopWallpaper =
|
||||||
@@ -1437,7 +1438,7 @@ impl WindowsApi {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_wallpaper(hmonitor: isize) -> Result<String> {
|
pub fn get_wallpaper(hmonitor: isize) -> eyre::Result<String> {
|
||||||
let wallpaper: IDesktopWallpaper =
|
let wallpaper: IDesktopWallpaper =
|
||||||
unsafe { CoCreateInstance(&DesktopWallpaper, None, CLSCTX_ALL)? };
|
unsafe { CoCreateInstance(&DesktopWallpaper, None, CLSCTX_ALL)? };
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ use windows::Win32::Foundation::HWND;
|
|||||||
use windows::Win32::Foundation::LPARAM;
|
use windows::Win32::Foundation::LPARAM;
|
||||||
use windows::Win32::Foundation::WPARAM;
|
use windows::Win32::Foundation::WPARAM;
|
||||||
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
|
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_EXSTYLE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
|
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::OBJID_WINDOW;
|
||||||
|
use windows::Win32::UI::WindowsAndMessaging::SendNotifyMessageW;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
|
use windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
|
||||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||||
@@ -33,8 +33,9 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
|||||||
if is_visible && is_window && !is_minimized {
|
if is_visible && is_window && !is_minimized {
|
||||||
let window = Window::from(hwnd);
|
let window = Window::from(hwnd);
|
||||||
|
|
||||||
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default()) {
|
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default())
|
||||||
if should_manage {
|
&& should_manage
|
||||||
|
{
|
||||||
if is_maximized {
|
if is_maximized {
|
||||||
WindowsApi::restore_window(window.hwnd);
|
WindowsApi::restore_window(window.hwnd);
|
||||||
}
|
}
|
||||||
@@ -44,7 +45,6 @@ pub extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
|||||||
containers.push_back(container);
|
containers.push_back(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
true.into()
|
true.into()
|
||||||
}
|
}
|
||||||
@@ -59,12 +59,12 @@ pub extern "system" fn alt_tab_windows(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
|||||||
if is_visible && is_window && !is_minimized {
|
if is_visible && is_window && !is_minimized {
|
||||||
let window = Window::from(hwnd);
|
let window = Window::from(hwnd);
|
||||||
|
|
||||||
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default()) {
|
if let Ok(should_manage) = window.should_manage(None, &mut RuleDebug::default())
|
||||||
if should_manage {
|
&& should_manage
|
||||||
|
{
|
||||||
windows.push(window);
|
windows.push(window);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
true.into()
|
true.into()
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user