Compare commits

..

1 Commits

Author SHA1 Message Date
LGUG2Z
2fe350edff feat(dpi): default to per monitor v2
This commit ensures that the process starts with
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 set by default.

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext

re #187
2022-07-23 10:47:23 -07:00
24 changed files with 256 additions and 1068 deletions

View File

@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]: Short descriptive title"
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See bug
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots and Videos**
Add screenshots and videos to help explain your problem.
**Operating System**
Provide the output of `systeminfo | grep "^OS Name\|^OS Version"`
For example:
```
OS Name: Microsoft Windows 11 Pro
OS Version: 10.0.22000 N/A Build 22000
```
**Additional context**
Add any other context about the problem here.
In particular, if you have any other AHK scripts or software running that handle any aspect of window management or manipulation

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEAT]: Short descriptive title"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

158
Cargo.lock generated
View File

@@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "3.2.16"
version = "3.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9"
checksum = "54635806b078b7925d6e36810b1755f2a4b5b4d57560432c1ecf60bcbe10602b"
dependencies = [
"atty",
"bitflags",
@@ -102,9 +102,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "3.2.15"
version = "3.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4"
checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
dependencies = [
"heck",
"proc-macro-error",
@@ -157,9 +157,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -167,9 +167,9 @@ dependencies = [
[[package]]
name = "crossbeam-deque"
version = "0.8.2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
@@ -178,9 +178,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.10"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
@@ -192,9 +192,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
dependencies = [
"cfg-if 1.0.0",
"once_cell",
@@ -241,9 +241,9 @@ dependencies = [
[[package]]
name = "dyn-clone"
version = "1.0.9"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
checksum = "9d07a982d1fb29db01e5a59b1918e03da4df7297eaeee7686ac45542fd4e59c8"
[[package]]
name = "either"
@@ -263,9 +263,9 @@ dependencies = [
[[package]]
name = "fastrand"
version = "1.8.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
@@ -448,9 +448,9 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "kernel32-sys"
@@ -464,7 +464,7 @@ dependencies = [
[[package]]
name = "komorebi"
version = "0.1.12"
version = "0.1.10"
dependencies = [
"bitflags",
"clap",
@@ -479,7 +479,6 @@ dependencies = [
"lazy_static",
"miow 0.4.0",
"nanoid",
"os_info",
"parking_lot",
"paste",
"schemars",
@@ -499,7 +498,7 @@ dependencies = [
[[package]]
name = "komorebi-core"
version = "0.1.12"
version = "0.1.10"
dependencies = [
"clap",
"color-eyre",
@@ -513,7 +512,7 @@ dependencies = [
[[package]]
name = "komorebic"
version = "0.1.12"
version = "0.1.10"
dependencies = [
"clap",
"color-eyre",
@@ -546,9 +545,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.129"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
@@ -746,17 +751,6 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "os_info"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5209b2162b2c140df493a93689e04f8deab3a67634f5bc7a553c0a98e5b8d399"
dependencies = [
"log",
"serde",
"winapi 0.3.9",
]
[[package]]
name = "os_str_bytes"
version = "6.2.0"
@@ -797,9 +791,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.8"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
[[package]]
name = "petgraph"
@@ -855,18 +849,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.43"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
@@ -927,9 +921,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.2.16"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
@@ -986,15 +980,15 @@ checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "rustversion"
version = "1.0.9"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8"
[[package]]
name = "ryu"
version = "1.0.11"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "same-file"
@@ -1037,18 +1031,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.143"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.143"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
dependencies = [
"proc-macro2",
"quote",
@@ -1068,9 +1062,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.83"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa",
"ryu",
@@ -1079,15 +1073,14 @@ dependencies = [
[[package]]
name = "serde_yaml"
version = "0.9.4"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79b7c9017c64a49806c6e8df8ef99b92446d09c92457f85f91835b01a8064ae0"
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
"yaml-rust",
]
[[package]]
@@ -1131,9 +1124,9 @@ dependencies = [
[[package]]
name = "strum_macros"
version = "0.24.3"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
checksum = "4faebde00e8ff94316c01800f9054fd2ba77d30d9e922541913051d1d978918b"
dependencies = [
"heck",
"proc-macro2",
@@ -1144,9 +1137,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.99"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
@@ -1155,9 +1148,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.25.2"
version = "0.24.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1594a36887d0f70096702bffadfb67dfbfe76ad4bf84605e86157dc9fce9961a"
checksum = "54cb4ebf3d49308b99e6e9dc95e989e2fdbdc210e4f67c39db0bb89ba927001c"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
@@ -1212,18 +1205,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.32"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
@@ -1252,9 +1245,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.13"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
dependencies = [
"itoa",
"libc",
@@ -1263,9 +1256,9 @@ dependencies = [
[[package]]
name = "tracing"
version = "0.1.36"
version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
@@ -1297,9 +1290,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.29"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
dependencies = [
"once_cell",
"valuable",
@@ -1356,15 +1349,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.3"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "unsafe-libyaml"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "931179334a56395bcf64ba5e0ff56781381c1a5832178280c7d7f91d1679aeb0"
checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7"
[[package]]
name = "valuable"
@@ -1605,3 +1592,12 @@ dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]

View File

@@ -4,5 +4,5 @@ members = [
"derive-ahk",
"komorebi",
"komorebi-core",
"komorebic",
"komorebic"
]

View File

@@ -2,14 +2,7 @@
Tiling Window Management for Windows.
![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/LGUG2Z/komorebi/Windows/master)
![GitHub](https://img.shields.io/github/license/LGUG2Z/komorebi)
![GitHub all releases](https://img.shields.io/github/downloads/LGUG2Z/komorebi/total)
![GitHub commits since latest release (by date) for a branch](https://img.shields.io/github/commits-since/LGUG2Z/komorebi/latest/master)
![Discord](https://img.shields.io/discord/898554690126630914?label=discord)
![GitHub Sponsors](https://img.shields.io/github/sponsors/LGUG2Z)
![screenshot](https://user-images.githubusercontent.com/13164844/184027064-f5a6cec2-2865-4d65-a549-a1f1da589abf.png)
![screenshot](https://i.ibb.co/BTqNS45/komorebi.png)
## About
@@ -34,18 +27,6 @@ Articles, blog posts, demos, and videos about _komorebi_ can be added to this li
- [Moving to Windows from Linux Pt 1](https://kvwu.io/posts/moving-to-windows/)
- [Windows 下的现代化平铺窗口管理器 komorebi](https://zhuanlan.zhihu.com/p/455064481)
## GitHub Sponsors Early Access
[GitHub Sponsors is enabled for this project](https://github.com/sponsors/LGUG2Z). Users who sponsor my work
on `komorebi` at any of the predefined monthly tiers will be given access to a private fork of this repository where I
push features-in-progress that are not yet quite ready to be pushed on the main repository.
There will never be any feature of `komorebi` that is gated behind sponsorship; every new feature will always be
available for free in the public repository once it meets the requisite level of code quality and completion.
Features-in-progress that are available in early access will be tagged in the issues with
an ["early access" label](https://github.com/LGUG2Z/komorebi/issues?q=is%3Aopen+is%3Aissue+label%3A%22early+access%22).
## Demonstrations
[@haxibami](https://github.com/haxibami) showing _komorebi_ running on Windows
@@ -258,17 +239,6 @@ If you already have configuration files that you wish to keep, move them to the
The next time you run `komorebic start`, any files created by or loaded by _komorebi_ will be placed or expected to
exist in this folder.
#### Adding an Active Window Border
If you would like to add a visual border around the currently focused window, two commands are available:
```powershell
komorebic.exe active-window-border [enable|disable]
komorebic.exe active-window-border-colour [R G B]
```
It is important to note that the active window border will only apply to windows managed by `komorebi`.
#### Removing Gaps
If you would like to remove all gaps from a given workspace, both between windows themselves, and between the monitor edges and the windows, you can set the following two configuration options to `0` for the desired monitors and workspaces:
@@ -278,19 +248,6 @@ komorebic.exe container-padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
komorebic.exe workspace padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
```
#### Multiple Layout Changes on Startup
Depending on what is in your configuration, when `komorebi` is started, you may experience the layout rapidly being adjusted
with many retile events.
If you would like to avoid this, you can start `komorebi` with a flag which tells `komorebi` to wait until all configuration
has been loaded before listening to and responding to window manager events: `komorebic start --await-configuration`.
If you start `komorebi` with the `--await-configuration` flag, you _must_ send the `komorebic complete-configuration`
command at the end of the configuration section of your `komorebi.ahk` config (before you start defining the key
bindings). The layout will not be updated and `komorebi` will not respond to `komorebic` commands until this command has
been received.
#### Floating Windows
Sometimes you will want a specific application to never be tiled, and instead float all the time. You add add rules to
@@ -438,9 +395,11 @@ YAML
```yaml
- column: Secondary
configuration: !Horizontal 2 # max number of rows
configuration:
Horizontal: 2 # max number of rows,
- column: Primary
configuration: !WidthPercentage 50 # percentage of screen
configuration:
WidthPercentage: 45 # percentage of screen
- column: Tertiary
configuration: Horizontal
```
@@ -544,7 +503,6 @@ manage Force komorebi to manage the focused
unmanage Unmanage a window that was forcibly managed
reload-configuration Reload ~/komorebi.ahk (if it exists)
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
complete-configuration Signal that the final configuration option has been sent
window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks
cross-monitor-move-behaviour Set the behaviour when moving windows across monitor boundaries
toggle-cross-monitor-move-behaviour Toggle the behaviour when moving windows across monitor boundaries
@@ -556,8 +514,6 @@ identify-object-name-change-application Identify an application that sends EV
identify-tray-application Identify an application that closes to the system tray
identify-layered-application Identify an application that has WS_EX_LAYERED, but should still be managed
identify-border-overflow-application Identify an application that has overflowing borders
active-window-border Enable or disable the active window border
active-window-border-colour Set the colour for the active window border
focus-follows-mouse Enable or disable focus follows mouse for the operating system
toggle-focus-follows-mouse Toggle focus follows mouse for the operating system
mouse-follows-focus Enable or disable mouse follows focus on all workspaces

View File

@@ -6,7 +6,7 @@ clean:
fmt:
cargo +nightly fmt
cargo +stable clippy
cargo +nightly clippy
prettier --write README.md
install-komorebic:

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.12"
version = "0.1.10"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,7 +10,7 @@ clap = { version = "3", features = ["derive"] }
color-eyre = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
serde_yaml = "0.8"
strum = { version = "0.24", features = ["derive"] }
schemars = "0.8"

View File

@@ -96,9 +96,6 @@ pub enum SocketMessage {
// Configuration
ReloadConfiguration,
WatchConfiguration(bool),
CompleteConfiguration,
ActiveWindowBorder(bool),
ActiveWindowBorderColour(u32, u32, u32),
InvisibleBorders(Rect),
WorkAreaOffset(Rect),
ResizeDelta(i32),
@@ -134,7 +131,7 @@ impl FromStr for SocketMessage {
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
pub enum StateQuery {
FocusedMonitorIndex,
@@ -143,7 +140,7 @@ pub enum StateQuery {
FocusedWindowIndex,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ApplicationIdentifier {
@@ -152,7 +149,7 @@ pub enum ApplicationIdentifier {
Title,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
pub enum FocusFollowsMouseImplementation {
Komorebi,
@@ -173,7 +170,7 @@ pub enum MoveBehaviour {
Insert,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
pub enum HidingBehaviour {
Hide,

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.12"
version = "0.1.10"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -23,23 +23,22 @@ dirs = "4"
getset = "0.1"
hotwatch = "0.4"
lazy_static = "1"
miow = "0.4"
nanoid = "0.4"
os_info = "3.4"
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
paste = "1"
schemars = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
strum = { version = "0.24", features = ["derive"] }
sysinfo = "0.25"
sysinfo = "0.24"
tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uds_windows = "1"
which = "4"
winput = "0.2"
miow = "0.4"
winreg = "0.10"
schemars = "0.8"
[dependencies.windows]
version = "0.39"
@@ -47,15 +46,12 @@ features = [
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_System_LibraryLoader",
"Win32_System_RemoteDesktop",
"Win32_System_Threading",
"Win32_System_RemoteDesktop",
"Win32_UI_Accessibility",
"Win32_UI_HiDpi",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_Shell_Common",
"Win32_UI_WindowsAndMessaging"
"Win32_UI_WindowsAndMessaging",
]
[features]

View File

@@ -1,119 +0,0 @@
use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::Result;
use komorebi_core::Rect;
use windows::core::PCSTR;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageA;
use windows::Win32::UI::WindowsAndMessaging::FindWindowA;
use windows::Win32::UI::WindowsAndMessaging::GetMessageA;
use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW;
use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use crate::window::Window;
use crate::windows_callbacks;
use crate::WindowsApi;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::WINDOWS_11;
#[derive(Debug, Clone, Copy)]
pub struct Border {
pub(crate) hwnd: isize,
}
impl From<isize> for Border {
fn from(hwnd: isize) -> Self {
Self { hwnd }
}
}
impl Border {
pub const fn hwnd(self) -> HWND {
HWND(self.hwnd)
}
pub fn create(name: &str) -> Result<()> {
let name = format!("{name}\0");
let instance = WindowsApi::module_handle_w()?;
let class_name = PCSTR(name.as_ptr());
let brush = WindowsApi::create_solid_brush(255, 140, 0);
let window_class = WNDCLASSA {
hInstance: instance,
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(windows_callbacks::border_window),
hbrBackground: brush,
..Default::default()
};
let _atom = WindowsApi::register_class_a(&window_class)?;
let name_cl = name.clone();
std::thread::spawn(move || -> Result<()> {
let hwnd = WindowsApi::create_border_window(PCSTR(name_cl.as_ptr()), instance)?;
let border = Self::from(hwnd);
let mut message = MSG::default();
unsafe {
while GetMessageA(&mut message, border.hwnd(), 0, 0).into() {
DispatchMessageA(&message);
std::thread::sleep(Duration::from_millis(10));
}
}
Ok(())
});
let mut hwnd = HWND(0);
while hwnd == HWND(0) {
hwnd = unsafe { FindWindowA(PCSTR(name.as_ptr()), PCSTR::null()) };
}
BORDER_HWND.store(hwnd.0, Ordering::SeqCst);
if *WINDOWS_11 {
WindowsApi::round_corners(hwnd.0)?;
}
Ok(())
}
pub fn hide(self) -> Result<()> {
WindowsApi::hide_border_window(self.hwnd())
}
pub fn set_position(
self,
window: Window,
invisible_borders: &Rect,
activate: bool,
) -> Result<()> {
let mut should_expand_border = false;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= invisible_borders.bottom;
rect.bottom += invisible_borders.bottom;
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
if border_overflows.contains(&window.title()?)
|| border_overflows.contains(&window.exe()?)
|| border_overflows.contains(&window.class()?)
{
should_expand_border = true;
}
if should_expand_border {
rect.left -= invisible_borders.left;
rect.top -= invisible_borders.top;
rect.right += invisible_borders.right;
rect.bottom += invisible_borders.bottom;
}
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
}
}

View File

@@ -7,11 +7,12 @@ use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
#[cfg(feature = "deadlock_detection")]
use std::thread;
#[cfg(feature = "deadlock_detection")]
use std::time::Duration;
use clap::Parser;
@@ -19,9 +20,7 @@ use color_eyre::eyre::anyhow;
use color_eyre::Result;
use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use crossbeam_utils::Backoff;
use lazy_static::lazy_static;
use os_info::Version;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
use parking_lot::Mutex;
@@ -40,7 +39,6 @@ use winreg::RegKey;
use komorebi_core::HidingBehaviour;
use komorebi_core::SocketMessage;
use crate::border::Border;
use crate::process_command::listen_for_commands;
use crate::process_event::listen_for_events;
use crate::process_movement::listen_for_movements;
@@ -52,7 +50,6 @@ use crate::windows_api::WindowsApi;
#[macro_use]
mod ring;
mod border;
mod container;
mod monitor;
mod process_command;
@@ -144,21 +141,10 @@ lazy_static! {
ahk_v2
};
static ref WINDOWS_11: bool = {
matches!(
os_info::get().version(),
Version::Semantic(_, _, x) if x >= &22000
)
};
}
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
@@ -256,7 +242,7 @@ pub fn load_configuration() -> Result<()> {
Command::new("AutoHotkey64.exe")
.arg(config_v2.as_os_str())
.output()?;
}
};
Ok(())
}
@@ -355,9 +341,9 @@ pub fn notify_subscribers(notification: &str) -> Result<()> {
#[tracing::instrument]
fn detect_deadlocks() {
// Create a background thread which checks for deadlocks every 10s
std::thread::spawn(move || loop {
thread::spawn(move || loop {
tracing::info!("running deadlock detector");
std::thread::sleep(Duration::from_secs(5));
thread::sleep(Duration::from_secs(5));
let deadlocks = deadlock::check_deadlock();
if deadlocks.is_empty() {
continue;
@@ -378,11 +364,8 @@ fn detect_deadlocks() {
#[clap(author, about, version)]
struct Opts {
/// Allow the use of komorebi's custom focus-follows-mouse implementation
#[clap(action, short, long = "ffm")]
#[clap(long = "ffm")]
focus_follows_mouse: bool,
/// Wait for 'komorebic complete-configuration' to be sent before processing events
#[clap(action, short, long)]
await_configuration: bool,
}
#[tracing::instrument]
@@ -391,9 +374,7 @@ fn main() -> Result<()> {
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
let arg_count = std::env::args().count();
let has_valid_args = arg_count == 1
|| (arg_count == 2 && (opts.await_configuration || opts.focus_follows_mouse))
|| (arg_count == 3 && opts.await_configuration && opts.focus_follows_mouse);
let has_valid_args = arg_count == 1 || (arg_count == 2 && CUSTOM_FFM.load(Ordering::SeqCst));
if has_valid_args {
let session_id = WindowsApi::process_id_to_session_id()?;
@@ -427,7 +408,6 @@ fn main() -> Result<()> {
let process_id = WindowsApi::current_process_id();
WindowsApi::allow_set_foreground_window(process_id)?;
WindowsApi::set_process_dpi_awareness_context()?;
Border::create("komorebi-border-window")?;
let (outgoing, incoming): (Sender<WindowManagerEvent>, Receiver<WindowManagerEvent>) =
crossbeam_channel::unbounded();
@@ -441,28 +421,14 @@ fn main() -> Result<()> {
wm.lock().init()?;
listen_for_commands(wm.clone());
if !opts.await_configuration && !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
};
std::thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
if opts.await_configuration {
let backoff = Backoff::new();
while !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
backoff.snooze();
}
}
listen_for_events(wm.clone());
if CUSTOM_FFM.load(Ordering::SeqCst) {
listen_for_movements(wm.clone());
}
load_configuration()?;
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
ctrlc::set_handler(move || {
ctrlc_sender

View File

@@ -7,6 +7,7 @@ use std::num::NonZeroUsize;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -27,23 +28,18 @@ use komorebi_core::SocketMessage;
use komorebi_core::StateQuery;
use komorebi_core::WindowContainerBehaviour;
use crate::border::Border;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::window::Window;
use crate::window_manager;
use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::BORDER_ENABLED;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::CUSTOM_FFM;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDING_BEHAVIOUR;
use crate::HOME_DIR;
use crate::INITIAL_CONFIGURATION_LOADED;
use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
@@ -59,7 +55,7 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
.try_clone()
.expect("could not clone unix listener");
std::thread::spawn(move || {
thread::spawn(move || {
tracing::info!("listening");
for client in listener.incoming() {
match client {
@@ -91,20 +87,6 @@ impl WindowManager {
}
}
match message {
SocketMessage::CycleFocusMonitor(_)
| SocketMessage::CycleFocusWorkspace(_)
| SocketMessage::FocusMonitorNumber(_)
| SocketMessage::FocusMonitorWorkspaceNumber(_, _)
| SocketMessage::FocusWorkspaceNumber(_) => {
if self.focused_workspace()?.visible_windows().is_empty() {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
}
}
_ => {}
};
match message {
SocketMessage::Promote => self.promote_container_to_front()?,
SocketMessage::FocusWindow(direction) => {
@@ -133,24 +115,24 @@ impl WindowManager {
SocketMessage::WorkspacePadding(monitor_idx, workspace_idx, size) => {
self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
}
SocketMessage::WorkspaceRule(_, ref id, monitor_idx, workspace_idx) => {
SocketMessage::WorkspaceRule(_, id, monitor_idx, workspace_idx) => {
{
let mut workspace_rules = WORKSPACE_RULES.lock();
workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx));
workspace_rules.insert(id, (monitor_idx, workspace_idx));
}
self.enforce_workspace_rules()?;
}
SocketMessage::ManageRule(_, ref id) => {
SocketMessage::ManageRule(_, id) => {
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
if !manage_identifiers.contains(id) {
manage_identifiers.push(id.to_string());
if !manage_identifiers.contains(&id) {
manage_identifiers.push(id);
}
}
SocketMessage::FloatRule(identifier, ref id) => {
SocketMessage::FloatRule(identifier, id) => {
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
if !float_identifiers.contains(id) {
float_identifiers.push(id.to_string());
if !float_identifiers.contains(&id) {
float_identifiers.push(id.clone());
}
let invisible_borders = self.invisible_borders;
@@ -167,17 +149,17 @@ impl WindowManager {
for window in container.windows().iter() {
match identifier {
ApplicationIdentifier::Exe => {
if window.exe()? == *id {
if window.exe()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
ApplicationIdentifier::Class => {
if window.class()? == *id {
if window.class()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
ApplicationIdentifier::Title => {
if window.title()? == *id {
if window.title()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
@@ -254,11 +236,9 @@ impl WindowManager {
SocketMessage::Retile => self.retile_all(false)?,
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
SocketMessage::ChangeLayoutCustom(ref path) => {
self.change_workspace_custom_layout(path.clone())?;
}
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, ref path) => {
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path.clone())?;
SocketMessage::ChangeLayoutCustom(path) => self.change_workspace_custom_layout(path)?,
SocketMessage::WorkspaceLayoutCustom(monitor_idx, workspace_idx, path) => {
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path)?;
}
SocketMessage::WorkspaceTiling(monitor_idx, workspace_idx, tile) => {
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
@@ -283,13 +263,13 @@ impl WindowManager {
monitor_idx,
workspace_idx,
at_container_count,
ref path,
path,
) => {
self.add_workspace_layout_custom_rule(
monitor_idx,
workspace_idx,
at_container_count,
path.clone(),
path,
)?;
}
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
@@ -353,8 +333,8 @@ impl WindowManager {
SocketMessage::NewWorkspace => {
self.new_workspace()?;
}
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, ref name) => {
self.set_workspace_name(monitor_idx, workspace_idx, name.to_string())?;
SocketMessage::WorkspaceName(monitor_idx, workspace_idx, name) => {
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
}
SocketMessage::State => {
let state = match serde_json::to_string_pretty(&window_manager::State::from(&*self))
@@ -594,37 +574,31 @@ impl WindowManager {
SocketMessage::ReloadConfiguration => {
Self::reload_configuration();
}
SocketMessage::CompleteConfiguration => {
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
self.update_focused_workspace(false)?;
}
}
SocketMessage::WatchConfiguration(enable) => {
self.watch_configuration(enable)?;
}
SocketMessage::IdentifyBorderOverflowApplication(_, ref id) => {
SocketMessage::IdentifyBorderOverflowApplication(_, id) => {
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
if !identifiers.contains(&id) {
identifiers.push(id);
}
}
SocketMessage::IdentifyObjectNameChangeApplication(_, ref id) => {
SocketMessage::IdentifyObjectNameChangeApplication(_, id) => {
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
if !identifiers.contains(&id) {
identifiers.push(id);
}
}
SocketMessage::IdentifyTrayApplication(_, ref id) => {
SocketMessage::IdentifyTrayApplication(_, id) => {
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
if !identifiers.contains(&id) {
identifiers.push(id);
}
}
SocketMessage::IdentifyLayeredApplication(_, ref id) => {
SocketMessage::IdentifyLayeredApplication(_, id) => {
let mut identifiers = LAYERED_WHITELIST.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
if !identifiers.contains(&id) {
identifiers.push(id);
}
}
SocketMessage::ManageFocusedWindow => {
@@ -674,7 +648,7 @@ impl WindowManager {
workspace.set_resize_dimensions(resize);
self.update_focused_workspace(false)?;
}
SocketMessage::Save(ref path) => {
SocketMessage::Save(path) => {
let workspace = self.focused_workspace_mut()?;
let resize = workspace.resize_dimensions();
@@ -682,14 +656,14 @@ impl WindowManager {
.write(true)
.truncate(true)
.create(true)
.open(path.clone())?;
.open(path)?;
serde_json::to_writer_pretty(&file, &resize)?;
}
SocketMessage::Load(ref path) => {
SocketMessage::Load(path) => {
let workspace = self.focused_workspace_mut()?;
let file = File::open(path)
let file = File::open(&path)
.map_err(|_| anyhow!("no file found at {}", path.display().to_string()))?;
let resize: Vec<Option<Rect>> = serde_json::from_reader(file)?;
@@ -697,18 +671,18 @@ impl WindowManager {
workspace.set_resize_dimensions(resize);
self.update_focused_workspace(false)?;
}
SocketMessage::AddSubscriber(ref subscriber) => {
SocketMessage::AddSubscriber(subscriber) => {
let mut pipes = SUBSCRIPTION_PIPES.lock();
let pipe_path = format!(r"\\.\pipe\{}", subscriber);
let pipe = connect(&pipe_path).map_err(|_| {
anyhow!("the named pipe '{}' has not yet been created; please create it before running this command", pipe_path)
})?;
pipes.insert(subscriber.clone(), pipe);
pipes.insert(subscriber, pipe);
}
SocketMessage::RemoveSubscriber(ref subscriber) => {
SocketMessage::RemoveSubscriber(subscriber) => {
let mut pipes = SUBSCRIPTION_PIPES.lock();
pipes.remove(subscriber);
pipes.remove(&subscriber);
}
SocketMessage::MouseFollowsFocus(enable) => {
self.mouse_follows_focus = enable;
@@ -749,19 +723,6 @@ impl WindowManager {
SocketMessage::UnmanagedWindowOperationBehaviour(behaviour) => {
self.unmanaged_window_operation_behaviour = behaviour;
}
SocketMessage::ActiveWindowBorder(enable) => {
if enable {
BORDER_ENABLED.store(true, Ordering::SeqCst);
self.show_border()?;
} else {
BORDER_ENABLED.store(false, Ordering::SeqCst);
self.hide_border()?;
}
}
SocketMessage::ActiveWindowBorderColour(r, g, b) => {
let hwnd = BORDER_HWND.load(Ordering::SeqCst);
WindowsApi::change_border_colour(hwnd, r, g, b)?;
}
SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification);
let schema = serde_json::to_string_pretty(&notification)?;
@@ -774,58 +735,6 @@ impl WindowManager {
}
};
match message {
SocketMessage::ChangeLayout(_)
| SocketMessage::ChangeLayoutCustom(_)
| SocketMessage::FlipLayout(_)
| SocketMessage::ManageFocusedWindow
| SocketMessage::MoveWorkspaceToMonitorNumber(_)
| SocketMessage::MoveContainerToMonitorNumber(_)
| SocketMessage::MoveContainerToWorkspaceNumber(_)
| SocketMessage::ResizeWindowEdge(_, _)
| SocketMessage::ResizeWindowAxis(_, _)
| SocketMessage::ToggleFloat
| SocketMessage::ToggleMonocle
| SocketMessage::ToggleMaximize
| SocketMessage::Promote
| SocketMessage::Retile
| SocketMessage::MoveWindow(_) => {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, &self.invisible_borders, false)?;
}
SocketMessage::TogglePause => {
let is_paused = self.is_paused;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
if is_paused {
border.hide()?;
} else {
let focused = self.focused_window()?;
border.set_position(*focused, &self.invisible_borders, true)?;
focused.focus(false)?;
}
}
SocketMessage::ToggleTiling | SocketMessage::WorkspaceTiling(..) => {
let tiling_enabled = *self.focused_workspace_mut()?.tile();
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
if tiling_enabled {
let focused = self.focused_window()?;
border.set_position(*focused, &self.invisible_borders, true)?;
focused.focus(false)?;
} else {
border.hide()?;
}
}
_ => {}
};
tracing::info!("processed");
Ok(())
}

View File

@@ -1,6 +1,6 @@
use std::fs::OpenOptions;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -12,7 +12,6 @@ use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::WindowContainerBehaviour;
use crate::border::Border;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::window_manager::WindowManager;
@@ -20,9 +19,6 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::BORDER_ENABLED;
use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
use crate::DATA_DIR;
use crate::HIDDEN_HWNDS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
@@ -31,7 +27,7 @@ use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
let receiver = wm.lock().incoming_events.lock().clone();
std::thread::spawn(move || {
thread::spawn(move || {
tracing::info!("listening");
loop {
select! {
@@ -474,46 +470,11 @@ impl WindowManager {
WindowManagerEvent::MonitorPoll(..) | WindowManagerEvent::MouseCapture(..) => {}
};
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
match event {
WindowManagerEvent::MoveResizeStart(_, _) => {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
BORDER_HIDDEN.store(true, Ordering::SeqCst);
}
WindowManagerEvent::MoveResizeEnd(_, _)
| WindowManagerEvent::Show(_, _)
| WindowManagerEvent::FocusChange(_, _) => {
let window = self.focused_window()?;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(*window, &self.invisible_borders, activate)?;
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
}
}
_ => {}
}
}
// If we unmanaged a window, it shouldn't be immediately hidden behind managed windows
if let WindowManagerEvent::Unmanage(window) = event {
window.center(&self.focused_monitor_work_area()?, &invisible_borders)?;
}
// If there are no more windows on the workspace, we shouldn't show the border window
if self.focused_workspace()?.containers().is_empty() {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
BORDER_HIDDEN.store(true, Ordering::SeqCst);
}
tracing::trace!("updating list of known hwnds");
let mut known_hwnds = vec![];
for monitor in self.monitors() {

View File

@@ -17,7 +17,7 @@ pub fn listen_for_movements(wm: Arc<Mutex<WindowManager>>) {
let receiver = message_loop::start().expect("could not start winput message loop");
loop {
let focus_follows_mouse = wm.lock().focus_follows_mouse;
let focus_follows_mouse = wm.lock().focus_follows_mouse.clone();
if let Some(FocusFollowsMouseImplementation::Komorebi) = focus_follows_mouse {
match receiver.next_event() {
// Don't want to send any raise events while we are dragging or resizing

View File

@@ -12,7 +12,6 @@ use serde::Serialize;
use serde::Serializer;
use windows::Win32::Foundation::HWND;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
@@ -175,18 +174,6 @@ impl Window {
WindowsApi::maximize_window(self.hwnd());
}
pub fn unmaximize(self) {
let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
if let Some(idx) = programmatically_hidden_hwnds
.iter()
.position(|&hwnd| hwnd == self.hwnd)
{
programmatically_hidden_hwnds.remove(idx);
}
WindowsApi::unmaximize_window(self.hwnd());
}
pub fn raise(self) -> Result<()> {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
@@ -243,17 +230,7 @@ impl Window {
}
// This isn't really needed when the above command works as expected via AHK
match WindowsApi::set_focus(self.hwnd()) {
Ok(_) => {}
Err(error) => {
tracing::error!(
"could not set focus, but continuing execution of focus(): {}",
error
);
}
};
Ok(())
WindowsApi::set_focus(self.hwnd())
}
#[allow(dead_code)]
@@ -316,7 +293,58 @@ impl Window {
// If not allowing cloaked windows, we need to ensure the window is not cloaked
(false, false) => {
if let (Ok(title), Ok(exe_name), Ok(class)) = (self.title(), self.exe(), self.class()) {
return Ok(window_is_eligible(&title, &exe_name, &class, self.style()?, self.ex_style()?, event));
let mut should_float = false;
{
let float_identifiers = FLOAT_IDENTIFIERS.lock();
for identifier in float_identifiers.iter() {
if title.starts_with(identifier) || title.ends_with(identifier) ||
class.starts_with(identifier) || class.ends_with(identifier) ||
identifier == &exe_name {
should_float = true;
}
}
}
let managed_override = {
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
manage_identifiers.contains(&exe_name)
|| manage_identifiers.contains(&class)
|| manage_identifiers.contains(&title)
};
if should_float && !managed_override {
return Ok(false);
}
let allow_layered = {
let layered_whitelist = LAYERED_WHITELIST.lock();
layered_whitelist.contains(&exe_name)
|| layered_whitelist.contains(&class)
|| layered_whitelist.contains(&title)
};
let allow_wsl2_gui = {
let wsl2_ui_processes = WSL2_UI_PROCESSES.lock();
wsl2_ui_processes.contains(&exe_name)
};
let style = self.style()?;
let ex_style = self.ex_style()?;
if (allow_wsl2_gui || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
&& !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME)
// Get a lot of dupe events coming through that make the redrawing go crazy
// on FocusChange events if I don't filter out this one. But, if we are
// allowing a specific layered window on the whitelist (like Steam), it should
// pass this check
&& (allow_layered || !ex_style.contains(ExtendedWindowStyle::LAYERED))
|| managed_override
{
return Ok(true);
} else if event.is_some() {
tracing::debug!("ignoring (exe: {}, title: {})", exe_name, title);
}
}
}
_ => {}
@@ -325,83 +353,3 @@ impl Window {
Ok(false)
}
}
fn window_is_eligible(
title: &String,
exe_name: &String,
class: &String,
style: WindowStyle,
ex_style: ExtendedWindowStyle,
event: Option<WindowManagerEvent>,
) -> bool {
let mut should_float = false;
let mut matched_identifier = None;
{
let float_identifiers = FLOAT_IDENTIFIERS.lock();
for identifier in float_identifiers.iter() {
if title.starts_with(identifier) || title.ends_with(identifier) {
should_float = true;
matched_identifier = Option::from(ApplicationIdentifier::Title);
}
if class.starts_with(identifier) || class.ends_with(identifier) {
should_float = true;
matched_identifier = Option::from(ApplicationIdentifier::Class);
}
if identifier == exe_name {
should_float = true;
matched_identifier = Option::from(ApplicationIdentifier::Exe);
}
}
};
let managed_override = {
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
matched_identifier.map_or_else(
|| {
manage_identifiers.contains(exe_name)
|| manage_identifiers.contains(class)
|| manage_identifiers.contains(title)
},
|matched_identifier| match matched_identifier {
ApplicationIdentifier::Exe => manage_identifiers.contains(exe_name),
ApplicationIdentifier::Class => manage_identifiers.contains(class),
ApplicationIdentifier::Title => manage_identifiers.contains(title),
},
)
};
if should_float && !managed_override {
return false;
}
let allow_layered = {
let layered_whitelist = LAYERED_WHITELIST.lock();
layered_whitelist.contains(exe_name)
|| layered_whitelist.contains(class)
|| layered_whitelist.contains(title)
};
let allow_wsl2_gui = {
let wsl2_ui_processes = WSL2_UI_PROCESSES.lock();
wsl2_ui_processes.contains(exe_name)
};
if (allow_wsl2_gui || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
&& !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME)
// Get a lot of dupe events coming through that make the redrawing go crazy
// on FocusChange events if I don't filter out this one. But, if we are
// allowing a specific layered window on the whitelist (like Steam), it should
// pass this check
&& (allow_layered || !ex_style.contains(ExtendedWindowStyle::LAYERED))
|| managed_override
{
return true;
} else if event.is_some() {
tracing::debug!("ignoring (exe: {}, title: {})", exe_name, title);
}
false
}

View File

@@ -2,8 +2,8 @@ use std::collections::VecDeque;
use std::io::ErrorKind;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -15,7 +15,6 @@ use schemars::JsonSchema;
use serde::Serialize;
use uds_windows::UnixListener;
use crate::border::Border;
use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement;
use komorebi_core::Axis;
@@ -40,7 +39,6 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
use crate::workspace::Workspace;
use crate::BORDER_HWND;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::DATA_DIR;
use crate::FLOAT_IDENTIFIERS;
@@ -107,7 +105,7 @@ impl From<&WindowManager> for State {
resize_delta: wm.resize_delta,
new_window_behaviour: wm.window_container_behaviour,
cross_monitor_move_behaviour: wm.cross_monitor_move_behaviour,
focus_follows_mouse: wm.focus_follows_mouse,
focus_follows_mouse: wm.focus_follows_mouse.clone(),
mouse_follows_focus: wm.mouse_follows_focus,
has_pending_raise_op: wm.has_pending_raise_op,
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
@@ -193,33 +191,14 @@ impl WindowManager {
pub fn init(&mut self) -> Result<()> {
tracing::info!("initialising");
WindowsApi::load_monitor_information(&mut self.monitors)?;
WindowsApi::load_workspace_information(&mut self.monitors)
}
#[tracing::instrument(skip(self))]
pub fn show_border(&self) -> Result<()> {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
let mut rect = WindowsApi::window_rect(foreground_window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, &self.invisible_borders, true)
}
#[tracing::instrument(skip(self))]
pub fn hide_border(&self) -> Result<()> {
let focused = self.focused_window()?;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
focused.focus(false)
WindowsApi::load_workspace_information(&mut self.monitors)?;
self.update_focused_workspace(false)
}
#[tracing::instrument]
pub fn reload_configuration() {
tracing::info!("reloading configuration");
std::thread::spawn(|| load_configuration().expect("could not load configuration"));
thread::spawn(|| load_configuration().expect("could not load configuration"));
}
#[tracing::instrument(skip(self))]
@@ -268,7 +247,7 @@ impl WindowManager {
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends
// a NoticeRemove, presumably because of the use of swap files?
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
std::thread::spawn(|| {
thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
}
@@ -290,7 +269,7 @@ impl WindowManager {
Ok(())
}
pub fn monitor_idx_in_direction(&self, direction: OperationDirection) -> Option<usize> {
pub fn monitor_index_in_direction(&self, direction: OperationDirection) -> Option<usize> {
let current_monitor_size = self.focused_monitor_size().ok()?;
for (idx, monitor) in self.monitors.elements().iter().enumerate() {
@@ -960,23 +939,16 @@ impl WindowManager {
pub fn focus_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace()?;
if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!(
"ignoring command while active window is in monocle mode or maximized"
));
}
tracing::info!("focusing container");
let workspace = self.focused_workspace()?;
let new_idx = workspace.new_idx_for_direction(direction);
// if there is no container in that direction for this workspace
match new_idx {
None => {
let monitor_idx = self
.monitor_idx_in_direction(direction)
.monitor_index_in_direction(direction)
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
self.focus_monitor(monitor_idx)?;
@@ -996,15 +968,10 @@ impl WindowManager {
pub fn move_container_in_direction(&mut self, direction: OperationDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace()?;
if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!(
"ignoring command while active window is in monocle mode or maximized"
));
}
tracing::info!("moving container");
let workspace = self.focused_workspace()?;
let origin_container_idx = workspace.focused_container_idx();
let origin_monitor_idx = self.focused_monitor_idx();
let target_container_idx = workspace.new_idx_for_direction(direction);
@@ -1014,7 +981,7 @@ impl WindowManager {
// in that direction if there is one
None => {
let target_monitor_idx = self
.monitor_idx_in_direction(direction)
.monitor_index_in_direction(direction)
.ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?;
{
@@ -1092,20 +1059,6 @@ impl WindowManager {
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.update_focused_workspace(offset, &invisible_borders)?;
let a = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor focused monitor"))?
.id();
let b = self
.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.id();
if !WindowsApi::monitors_have_same_scale_factor(a, b)? {
self.update_focused_workspace(self.mouse_follows_focus)?;
}
}
Some(new_idx) => {
let workspace = self.focused_workspace_mut()?;
@@ -1158,15 +1111,10 @@ impl WindowManager {
pub fn move_container_in_cycle_direction(&mut self, direction: CycleDirection) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace_mut()?;
if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!(
"ignoring command while active window is in monocle mode or maximized"
));
}
tracing::info!("moving container");
let workspace = self.focused_workspace_mut()?;
let current_idx = workspace.focused_container_idx();
let new_idx = workspace
.new_idx_for_cycle_direction(direction)
@@ -1475,7 +1423,6 @@ impl WindowManager {
}
workspace.set_layout(Layout::Custom(layout));
workspace.set_layout_flip(None);
self.update_focused_workspace(self.mouse_follows_focus)
}
@@ -1716,7 +1663,6 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no monitor"))?;
workspace.set_layout(Layout::Custom(layout));
workspace.set_layout_flip(None);
// If this is the focused workspace on a non-focused screen, let's update it
if focused_monitor_idx != monitor_idx && focused_workspace_idx == workspace_idx {
@@ -1911,7 +1857,7 @@ impl WindowManager {
monitor.focus_workspace(idx)?;
monitor.load_focused_workspace(mouse_follows_focus)?;
self.update_focused_workspace(false)
self.update_focused_workspace(mouse_follows_focus)
}
#[tracing::instrument(skip(self))]

View File

@@ -6,37 +6,29 @@ use color_eyre::eyre::anyhow;
use color_eyre::eyre::Error;
use color_eyre::Result;
use windows::core::Result as WindowsCrateResult;
use windows::core::PCSTR;
use windows::core::PWSTR;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::POINT;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
use windows::Win32::Graphics::Dwm::DWMWA_WINDOW_CORNER_PREFERENCE;
use windows::Win32::Graphics::Dwm::DWMWCP_ROUND;
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
use windows::Win32::Graphics::Gdi::CreateSolidBrush;
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
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::MONITORINFO;
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
use windows::Win32::System::Threading::AttachThreadInput;
use windows::Win32::System::Threading::GetCurrentProcessId;
@@ -49,10 +41,7 @@ use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
@@ -67,8 +56,6 @@ use windows::Win32::UI::WindowsAndMessaging::IsIconic;
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
use windows::Win32::UI::WindowsAndMessaging::SetClassLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
@@ -76,12 +63,9 @@ use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT;
use windows::Win32::UI::WindowsAndMessaging::GCLP_HBRBACKGROUND;
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::HWND_BOTTOM;
use windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
@@ -92,18 +76,11 @@ use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
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::SW_RESTORE;
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::WNDCLASSA;
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
use komorebi_core::Rect;
@@ -134,7 +111,7 @@ macro_rules! impl_from_integer_for_windows_result {
};
}
impl_from_integer_for_windows_result!(usize, isize, u16, u32, i32);
impl_from_integer_for_windows_result!(isize, u32, i32);
impl<T, E> From<WindowsResult<T, E>> for Result<T, E> {
fn from(result: WindowsResult<T, E>) -> Self {
@@ -283,24 +260,6 @@ impl WindowsApi {
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
pub fn position_border_window(hwnd: HWND, layout: &Rect, activate: bool) -> Result<()> {
let flags = if activate {
SetWindowPosition::SHOW_WINDOW | SetWindowPosition::NO_ACTIVATE
} else {
SetWindowPosition::NO_ACTIVATE
};
let position = HWND_BOTTOM;
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
pub fn hide_border_window(hwnd: HWND) -> Result<()> {
let flags = SetWindowPosition::HIDE_WINDOW;
let position = HWND_BOTTOM;
Self::set_window_pos(hwnd, &Rect::default(), position, flags.bits())
}
pub fn set_window_pos(hwnd: HWND, layout: &Rect, position: HWND, flags: u32) -> Result<()> {
unsafe {
SetWindowPos(
@@ -332,11 +291,7 @@ impl WindowsApi {
}
pub fn restore_window(hwnd: HWND) {
Self::show_window(hwnd, SW_SHOWNOACTIVATE);
}
pub fn unmaximize_window(hwnd: HWND) {
Self::show_window(hwnd, SW_NORMAL);
Self::show_window(hwnd, SW_RESTORE);
}
pub fn maximize_window(hwnd: HWND) {
@@ -483,11 +438,6 @@ impl WindowsApi {
Self::set_window_long_ptr_w(hwnd, GWL_STYLE, new_value)
}
#[allow(dead_code)]
pub fn update_ex_style(hwnd: HWND, new_value: isize) -> Result<()> {
Self::set_window_long_ptr_w(hwnd, GWL_EXSTYLE, new_value)
}
pub fn window_text_w(hwnd: HWND) -> Result<String> {
let mut text: [u16; 512] = [0; 512];
match WindowsResult::from(unsafe { GetWindowTextW(hwnd, &mut text) }) {
@@ -668,71 +618,4 @@ impl WindowsApi {
SPIF_SENDCHANGE,
)
}
pub fn module_handle_w() -> Result<HINSTANCE> {
unsafe { GetModuleHandleW(None) }.process()
}
pub fn create_solid_brush(r: u32, g: u32, b: u32) -> HBRUSH {
unsafe { CreateSolidBrush(r | (g << 8) | (b << 16)) }
}
pub fn register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
Result::from(WindowsResult::from(unsafe { RegisterClassA(window_class) }))
}
pub fn scale_factor_for_monitor(hmonitor: isize) -> Result<DEVICE_SCALE_FACTOR> {
unsafe { GetScaleFactorForMonitor(HMONITOR(hmonitor)) }.process()
}
pub fn monitors_have_same_scale_factor(a: isize, b: isize) -> Result<bool> {
let a = Self::scale_factor_for_monitor(a)?;
let b = Self::scale_factor_for_monitor(b)?;
Ok(a == b)
}
pub fn change_border_colour(hwnd: isize, r: u32, g: u32, b: u32) -> Result<usize> {
Result::from(WindowsResult::from(unsafe {
SetClassLongPtrW(
HWND(hwnd),
GCLP_HBRBACKGROUND,
Self::create_solid_brush(r, g, b).0,
)
}))
}
pub fn round_corners(hwnd: isize) -> Result<()> {
let round = DWMWCP_ROUND;
unsafe {
DwmSetWindowAttribute(
HWND(hwnd),
DWMWA_WINDOW_CORNER_PREFERENCE,
std::ptr::addr_of!(round).cast(),
4,
)
}
.process()
}
pub fn create_border_window(name: PCSTR, instance: HINSTANCE) -> Result<isize> {
unsafe {
CreateWindowExA(
WS_EX_TOOLWINDOW,
name,
name,
WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
None,
None,
instance,
std::ptr::null(),
)
}
.process()
}
}

View File

@@ -3,17 +3,10 @@ use std::collections::VecDeque;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::LRESULT;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Gdi::InvalidateRect;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
use crate::container::Container;
use crate::monitor::Monitor;
@@ -110,24 +103,3 @@ pub extern "system" fn win_event_hook(
}
}
}
pub extern "system" fn border_window(
window: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
unsafe {
match message as u32 {
WM_PAINT => {
InvalidateRect(window, std::ptr::null(), true);
LRESULT(0)
}
WM_DESTROY => {
PostQuitMessage(0);
LRESULT(0)
}
_ => DefWindowProcW(window, message, wparam, lparam),
}
}
}

View File

@@ -1,6 +1,7 @@
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
use std::time::Duration;
use crossbeam_channel::Receiver;
@@ -43,10 +44,10 @@ impl WinEventListener {
let hook = self.hook.clone();
let outgoing = self.outgoing_events.lock().clone();
std::thread::spawn(move || unsafe {
thread::spawn(move || unsafe {
let hook_ref = SetWinEventHook(
EVENT_MIN,
EVENT_MAX,
EVENT_MIN as u32,
EVENT_MAX as u32,
None,
Some(windows_callbacks::win_event_hook),
0,
@@ -95,7 +96,7 @@ impl MessageLoop {
}
}
std::thread::sleep(Duration::from_millis(sleep));
thread::sleep(Duration::from_millis(sleep));
if !cb(value) {
break;

View File

@@ -1,6 +1,5 @@
use std::collections::VecDeque;
use std::num::NonZeroUsize;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -22,7 +21,6 @@ use crate::container::Container;
use crate::ring::Ring;
use crate::window::Window;
use crate::windows_api::WindowsApi;
use crate::INITIAL_CONFIGURATION_LOADED;
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters, JsonSchema)]
pub struct Workspace {
@@ -151,10 +149,6 @@ impl Workspace {
offset: Option<Rect>,
invisible_borders: &Rect,
) -> Result<()> {
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
return Ok(());
}
let container_padding = self.container_padding();
let mut adjusted_work_area = offset.map_or_else(
|| *work_area,
@@ -183,10 +177,6 @@ impl Workspace {
}
if let Some(updated_layout) = updated_layout {
if !matches!(updated_layout, Layout::Default(DefaultLayout::BSP)) {
self.set_layout_flip(None);
}
self.set_layout(updated_layout);
}
}
@@ -366,23 +356,6 @@ impl Workspace {
false
}
pub fn is_focused_window_monocle_or_maximized(&self) -> Result<bool> {
let hwnd = WindowsApi::foreground_window()?;
if let Some(window) = self.maximized_window() {
if hwnd == window.hwnd {
return Ok(true);
}
}
if let Some(container) = self.monocle_container() {
if container.contains_window(hwnd) {
return Ok(true);
}
}
Ok(false)
}
pub fn contains_window(&self, hwnd: isize) -> bool {
for container in self.containers() {
if container.contains_window(hwnd) {
@@ -493,7 +466,6 @@ impl Workspace {
if let Some(window) = self.maximized_window() {
if window.hwnd == hwnd {
window.unmaximize();
self.set_maximized_window(None);
self.set_maximized_window_restore_idx(None);
return Ok(());
@@ -675,44 +647,22 @@ impl Workspace {
}
pub fn new_floating_window(&mut self) -> Result<()> {
let window = if let Some(maximized_window) = self.maximized_window() {
let window = *maximized_window;
self.set_maximized_window(None);
self.set_maximized_window_restore_idx(None);
window
} else if let Some(monocle_container) = self.monocle_container_mut() {
let window = monocle_container
.remove_focused_window()
.ok_or_else(|| anyhow!("there is no window"))?;
let focused_idx = self.focused_container_idx();
if monocle_container.windows().is_empty() {
self.set_monocle_container(None);
self.set_monocle_container_restore_idx(None);
} else {
monocle_container.load_focused_window();
}
let container = self
.focused_container_mut()
.ok_or_else(|| anyhow!("there is no container"))?;
window
let window = container
.remove_focused_window()
.ok_or_else(|| anyhow!("there is no window"))?;
if container.windows().is_empty() {
self.containers_mut().remove(focused_idx);
self.resize_dimensions_mut().remove(focused_idx);
} else {
let focused_idx = self.focused_container_idx();
let container = self
.focused_container_mut()
.ok_or_else(|| anyhow!("there is no container"))?;
let window = container
.remove_focused_window()
.ok_or_else(|| anyhow!("there is no window"))?;
if container.windows().is_empty() {
self.containers_mut().remove(focused_idx);
self.resize_dimensions_mut().remove(focused_idx);
} else {
container.load_focused_window();
}
window
};
container.load_focused_window();
}
self.floating_windows_mut().push(window);
@@ -798,53 +748,6 @@ impl Workspace {
pub fn new_maximized_window(&mut self) -> Result<()> {
let focused_idx = self.focused_container_idx();
let foreground_hwnd = WindowsApi::foreground_window()?;
let mut floating_window = None;
if !self.floating_windows().is_empty() {
let mut focused_floating_window_idx = None;
for (i, w) in self.floating_windows().iter().enumerate() {
if w.hwnd == foreground_hwnd {
focused_floating_window_idx = Option::from(i);
}
}
if let Some(idx) = focused_floating_window_idx {
floating_window = Option::from(self.floating_windows_mut().remove(idx));
}
}
if let Some(floating_window) = floating_window {
self.set_maximized_window(Option::from(floating_window));
self.set_maximized_window_restore_idx(Option::from(focused_idx));
if let Some(window) = self.maximized_window() {
window.maximize();
}
return Ok(());
}
let monocle_restore_idx = self.monocle_container_restore_idx();
if let Some(monocle_container) = self.monocle_container_mut() {
let window = monocle_container
.remove_focused_window()
.ok_or_else(|| anyhow!("there is no window"))?;
if monocle_container.windows().is_empty() {
self.set_monocle_container(None);
self.set_monocle_container_restore_idx(None);
} else {
monocle_container.load_focused_window();
}
self.set_maximized_window(Option::from(window));
self.set_maximized_window_restore_idx(monocle_restore_idx);
if let Some(window) = self.maximized_window() {
window.maximize();
}
return Ok(());
}
let container = self
.focused_container_mut()

View File

@@ -1,7 +1,7 @@
; Generated by komorebic.exe
Start(ffm, await_configuration) {
Run, komorebic.exe start %ffm% --await_configuration %await_configuration%, , Hide
Start(ffm) {
Run, komorebic.exe start --ffm %ffm%, , Hide
}
Stop() {
@@ -252,10 +252,6 @@ WatchConfiguration(boolean_state) {
Run, komorebic.exe watch-configuration %boolean_state%, , Hide
}
CompleteConfiguration() {
Run, komorebic.exe complete-configuration, , Hide
}
WindowHidingBehaviour(hiding_behaviour) {
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
}
@@ -300,14 +296,6 @@ IdentifyBorderOverflowApplication(identifier, id) {
Run, komorebic.exe identify-border-overflow-application %identifier% "%id%", , Hide
}
ActiveWindowBorder(boolean_state) {
Run, komorebic.exe active-window-border %boolean_state%, , Hide
}
ActiveWindowBorderColour(r, g, b) {
Run, komorebic.exe active-window-border-colour %r% %g% %b%, , Hide
}
FocusFollowsMouse(boolean_state, implementation) {
Run, komorebic.exe focus-follows-mouse %boolean_state% --implementation %implementation%, , Hide
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.12"
version = "0.1.10"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
categories = ["cli", "tiling-window-manager", "windows"]
@@ -24,7 +24,7 @@ paste = "1"
powershell_script = "1.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
serde_yaml = "0.8"
uds_windows = "1"
[dependencies.windows]

View File

@@ -393,30 +393,11 @@ struct FocusFollowsMouse {
boolean_state: BooleanState,
}
#[derive(Parser, AhkFunction)]
struct ActiveWindowBorder {
#[clap(arg_enum)]
boolean_state: BooleanState,
}
#[derive(Parser, AhkFunction)]
struct ActiveWindowBorderColour {
/// Red
r: u32,
/// Green
g: u32,
/// Blue
b: u32,
}
#[derive(Parser, AhkFunction)]
struct Start {
/// Allow the use of komorebi's custom focus-follows-mouse implementation
#[clap(action, short, long = "ffm")]
#[clap(long)]
ffm: bool,
/// Wait for 'komorebic complete-configuration' to be sent before processing events
#[clap(action, short, long)]
await_configuration: bool,
}
#[derive(Parser, AhkFunction)]
@@ -646,8 +627,6 @@ enum SubCommand {
/// Enable or disable watching of ~/komorebi.ahk (if it exists)
#[clap(arg_required_else_help = true)]
WatchConfiguration(WatchConfiguration),
/// Signal that the final configuration option has been sent
CompleteConfiguration,
/// Set the window behaviour when switching workspaces / cycling stacks
#[clap(arg_required_else_help = true)]
WindowHidingBehaviour(WindowHidingBehaviour),
@@ -681,12 +660,6 @@ enum SubCommand {
#[clap(arg_required_else_help = true)]
#[clap(alias = "identify-border-overflow")]
IdentifyBorderOverflowApplication(IdentifyBorderOverflowApplication),
/// Enable or disable the active window border
#[clap(arg_required_else_help = true)]
ActiveWindowBorder(ActiveWindowBorder),
/// Set the colour for the active window border
#[clap(arg_required_else_help = true)]
ActiveWindowBorderColour(ActiveWindowBorderColour),
/// Enable or disable focus follows mouse for the operating system
#[clap(arg_required_else_help = true)]
FocusFollowsMouse(FocusFollowsMouse),
@@ -937,33 +910,19 @@ fn main() -> Result<()> {
let script = exec.map_or_else(
|| {
if arg.ffm | arg.await_configuration {
format!(
"Start-Process komorebi.exe -ArgumentList {} -WindowStyle hidden",
if arg.ffm && arg.await_configuration {
"'--ffm','--await-configuration'"
} else if arg.ffm {
"'--ffm'"
} else {
"'--await-configuration'"
}
if arg.ffm {
String::from(
"Start-Process komorebi.exe -ArgumentList '--ffm' -WindowStyle hidden",
)
} else {
String::from("Start-Process komorebi.exe -WindowStyle hidden")
}
},
|exec| {
if arg.ffm | arg.await_configuration {
if arg.ffm {
format!(
"Start-Process '{}' -ArgumentList {} -WindowStyle hidden",
exec,
if arg.ffm && arg.await_configuration {
"'--ffm','--await-configuration'"
} else if arg.ffm {
"'--ffm'"
} else {
"'--await-configuration'"
}
"Start-Process '{}' -ArgumentList '--ffm' -WindowStyle hidden",
exec
)
} else {
format!("Start-Process '{}' -WindowStyle hidden", exec)
@@ -1148,9 +1107,6 @@ fn main() -> Result<()> {
SubCommand::WatchConfiguration(arg) => {
send_message(&SocketMessage::WatchConfiguration(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::CompleteConfiguration => {
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
}
SubCommand::IdentifyObjectNameChangeApplication(target) => {
send_message(
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
@@ -1204,14 +1160,6 @@ fn main() -> Result<()> {
SubCommand::MouseFollowsFocus(arg) => {
send_message(&SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::ActiveWindowBorder(arg) => {
send_message(&SocketMessage::ActiveWindowBorder(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::ActiveWindowBorderColour(arg) => {
send_message(
&SocketMessage::ActiveWindowBorderColour(arg.r, arg.g, arg.b).as_bytes()?,
)?;
}
SubCommand::ResizeDelta(arg) => {
send_message(&SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
}
@@ -1345,18 +1293,15 @@ fn resolve_windows_path(raw_path: &str) -> Result<PathBuf> {
.parent()
.ok_or_else(|| anyhow!("cannot parse directory"))?;
Ok(if parent.is_dir() {
let file = full_path
.components()
.last()
.ok_or_else(|| anyhow!("cannot parse filename"))?;
let file = full_path
.components()
.last()
.ok_or_else(|| anyhow!("cannot parse filename"))?;
let mut canonicalized = std::fs::canonicalize(parent)?;
canonicalized.push(file);
canonicalized
} else {
full_path
})
let mut canonicalized = std::fs::canonicalize(parent)?;
canonicalized.push(file);
Ok(canonicalized)
}
fn show_window(hwnd: HWND, command: SHOW_WINDOW_CMD) {

View File

@@ -1,2 +0,0 @@
[toolchain]
channel = "stable"