Compare commits

...

193 Commits

Author SHA1 Message Date
LGUG2Z
096729c2bd chore(release): v0.1.16 2023-05-08 08:58:16 -07:00
LGUG2Z
de5efd9b35 docs(asc): update generated rules 2023-05-08 08:56:39 -07:00
dependabot[bot]
294c14e6a6 chore(deps): bump sysinfo from 0.28.4 to 0.29.0
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.28.4 to 0.29.0.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 08:48:24 -07:00
dependabot[bot]
4d26cdee32 chore(deps): bump serde from 1.0.160 to 1.0.162
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.160 to 1.0.162.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.160...1.0.162)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 08:48:10 -07:00
dependabot[bot]
6748d7e4a9 chore(deps): bump clap from 4.2.5 to 4.2.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.5 to 4.2.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.5...v4.2.7)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-08 08:47:58 -07:00
dependabot[bot]
7f350341bb chore(deps): bump clap from 4.2.4 to 4.2.5
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.4 to 4.2.5.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.4...v4.2.5)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 10:31:56 -07:00
dependabot[bot]
33dcadfce3 chore(deps): bump dirs from 5.0.0 to 5.0.1
Bumps [dirs](https://github.com/soc/dirs-rs) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/soc/dirs-rs/releases)
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 10:31:42 -07:00
dependabot[bot]
52236679a5 chore(deps): bump bitflags from 1.3.2 to 2.2.1
Bumps [bitflags](https://github.com/bitflags/bitflags) from 1.3.2 to 2.2.1.
- [Release notes](https://github.com/bitflags/bitflags/releases)
- [Changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bitflags/bitflags/compare/1.3.2...2.2.1)

---
updated-dependencies:
- dependency-name: bitflags
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-05-01 06:08:39 -07:00
LGUG2Z
9431bac4ad docs(readme): add section on using ahk 2023-04-30 13:06:45 -07:00
LGUG2Z
e4a9719f4f feat(ahk): add sample v2 configuration file 2023-04-30 13:06:45 -07:00
LGUG2Z
e044a5a16f fix(ahk): implement syntax corrections by @sitiom
This commit implements the suggestions made by @sitiom on Discord:
https://discord.com/channels/898554690126630914/898554690608967786/1097403455267667968

re #324
2023-04-30 13:06:45 -07:00
LGUG2Z
60d3ecd8aa feat(ahk): update cfgen, reintroduce ahk files
This commit updates the config generator used by the ahk-asc command to
emit AHKv2 syntax.

An AHKv2 syntax-compatible komorebic.lib.ahk has been (re)introduced to
the repo root as a file to be distributed. This file is created by
taking the AHKv1 syntax output of ahk-library and automatically
converting it to AHKv2 using the automatic script converter by @mmikeww
available on GitHub.

Given that ahk-library is still being used to emit AHKv1 syntax in this
pipeline, it will remain in the repo.

The justfile has been updated to automate as much of this as possible
(the converter still needs to be run manually).

re #324
2023-04-30 13:06:45 -07:00
Alvin Truong
27e0758089 feat(workspace-rules): add InitialWorkspaceRule and InitialNamedWorkspaceRule command
- Added the two new commands to prevent breaking changes due to AHK
  users.
2023-04-30 12:40:01 -07:00
Alvin Truong
7d1aef8203 refactor(workspace_rules): reduce code duplication 2023-04-30 12:40:01 -07:00
Alvin Truong
b273617f44 style: cargo +nightly fmt --all 2023-04-30 12:40:01 -07:00
Alvin Truong
4306a7bafe feat(workspace-rule): add ability to only apply workspace rule on first
show of app
2023-04-30 12:40:01 -07:00
LGUG2Z
b647fdf01a feat(cli): add check command for useful info
This commit adds a new komorebic command, check, to output useful
information that will aid in understanding unexpected behaviour reported
by users.
2023-04-28 17:43:27 -07:00
LGUG2Z
9f16cb91a9 docs(readme): embed more tutorial videos 2023-04-26 11:53:22 -07:00
LGUG2Z
2520c4abe1 feat(wm): add border colour for monocle
This commit adds a new WindowKind to allow the user to set a unique
colour to identify when a window container in monocle mode is active.

resolve #404
2023-04-24 13:08:29 -07:00
dependabot[bot]
d32661ec2d chore(deps): bump tracing-subscriber from 0.3.16 to 0.3.17
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.16 to 0.3.17.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.16...tracing-subscriber-0.3.17)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-24 08:20:03 -07:00
dependabot[bot]
9df99f28cf chore(deps): bump clap from 4.2.2 to 4.2.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.2.2 to 4.2.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.2.2...v4.2.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-24 08:19:44 -07:00
جاد
7327bb9a70 docs(readme): add link to quickstart video on youtube 2023-04-20 12:28:28 -07:00
dependabot[bot]
b9a9d20c66 chore(deps): bump dirs from 4.0.0 to 5.0.0
Bumps [dirs](https://github.com/soc/dirs-rs) from 4.0.0 to 5.0.0.
- [Release notes](https://github.com/soc/dirs-rs/releases)
- [Commits](https://github.com/soc/dirs-rs/commits)

---
updated-dependencies:
- dependency-name: dirs
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-17 08:44:42 -07:00
Alex Rumak
f89224c5d4 Fix default whkd configuration file
The whkd process does not start with an s - unless something is wrong or buggy on my computer, it is just "whkd.exe" and so it should be that in this file as well.
2023-04-16 13:19:24 -07:00
LGUG2Z
e68cf6fa91 chore(deps): bump windows crates from 0.44 to 0.48 2023-04-15 21:47:37 -07:00
LGUG2Z
b50326ed27 chore(deps): cargo update 2023-04-15 21:28:06 -07:00
LGUG2Z
66f2395840 fix(wm): update border correctly on (un)stack
This commit ensures that when active, the border is drawn correctly
after the stack and unstack commands.

fix #396
2023-04-15 21:26:38 -07:00
Alvin Truong
7828c403ba fix(komorebic): global-work-area-offset duplicate command and alias runtime error 2023-04-15 19:54:18 -07:00
dependabot[bot]
366cd4ff91 chore(deps): bump winreg from 0.11.0 to 0.50.0
Bumps [winreg](https://github.com/gentoo90/winreg-rs) from 0.11.0 to 0.50.0.
- [Release notes](https://github.com/gentoo90/winreg-rs/releases)
- [Changelog](https://github.com/gentoo90/winreg-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gentoo90/winreg-rs/compare/v0.11.0...v0.50.0)

---
updated-dependencies:
- dependency-name: winreg
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 14:00:00 -07:00
dependabot[bot]
a3ee513003 chore(deps): bump os_info from 3.6.0 to 3.7.0
Bumps [os_info](https://github.com/stanislav-tkach/os_info) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/stanislav-tkach/os_info/releases)
- [Changelog](https://github.com/stanislav-tkach/os_info/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stanislav-tkach/os_info/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: os_info
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-10 13:59:17 -07:00
dependabot[bot]
0de3fa62f0 chore(deps): bump proc-macro2 from 1.0.54 to 1.0.56
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.54 to 1.0.56.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.54...1.0.56)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-04 08:58:44 -07:00
dependabot[bot]
aadf80f3be chore(deps): bump clap from 4.1.13 to 4.2.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.13 to 4.2.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.13...v4.2.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-04 08:58:27 -07:00
dependabot[bot]
0a3f27d5ad chore(deps): bump sysinfo from 0.28.1 to 0.28.4
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.28.1 to 0.28.4.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-04 08:57:59 -07:00
dependabot[bot]
9c5b380412 chore(deps): bump serde_json from 1.0.93 to 1.0.94
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.93 to 1.0.94.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.93...v1.0.94)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-28 12:18:53 -07:00
dependabot[bot]
587c5a2636 chore(deps): bump serde_yaml from 0.9.17 to 0.9.19
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.17 to 0.9.19.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.17...0.9.19)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-28 12:18:42 -07:00
dependabot[bot]
b5ca0bfd45 chore(deps): bump paste from 1.0.11 to 1.0.12
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.11 to 1.0.12.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.11...1.0.12)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-28 12:18:28 -07:00
dependabot[bot]
30fc1ef538 chore(deps): bump crossbeam-utils from 0.8.14 to 0.8.15
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.14 to 0.8.15.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.14...crossbeam-utils-0.8.15)

---
updated-dependencies:
- dependency-name: crossbeam-utils
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 16:47:05 -07:00
dependabot[bot]
76d0a38165 chore(deps): bump clap from 4.1.6 to 4.1.13
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.6 to 4.1.13.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.6...v4.1.13)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 16:46:47 -07:00
dependabot[bot]
34d65dddba chore(deps): bump syn from 1.0.107 to 1.0.109
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.107 to 1.0.109.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.107...1.0.109)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 13:26:53 -08:00
dependabot[bot]
a8e7f02b0a chore(deps): bump sysinfo from 0.28.0 to 0.28.1
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.28.0 to 0.28.1.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 13:26:33 -08:00
dependabot[bot]
1a59b3a2e3 chore(deps): bump schemars from 0.8.11 to 0.8.12
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.11 to 0.8.12.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.11...v0.8.12)

---
updated-dependencies:
- dependency-name: schemars
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 13:26:12 -08:00
LGUG2Z
cd7606540a docs(whkd): set powershell as default in whkdrc
A number of people have been tripped up by not having the latest version
of PowerShell installed, which is referenced by the binary "pwsh", so
this commit sets the default .shell in whkdrc.sample to "powershell"
which _should_ come installed with every version of Windows since
Windows 10.

resolve #365
2023-02-27 13:24:49 -08:00
LGUG2Z
f41e8c151e feat(wm): add cycle move/send to monitor cmds
This commit introduces cyclical commands for moving (with focus) or
sending (without focus) windows between adjacent monitors.

resolve #363
2023-02-24 16:59:16 -08:00
dependabot[bot]
8ce49f5868 chore(deps): bump winreg from 0.10.1 to 0.11.0
Bumps [winreg](https://github.com/gentoo90/winreg-rs) from 0.10.1 to 0.11.0.
- [Release notes](https://github.com/gentoo90/winreg-rs/releases)
- [Changelog](https://github.com/gentoo90/winreg-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gentoo90/winreg-rs/compare/v0.10.1...v0.11.0)

---
updated-dependencies:
- dependency-name: winreg
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 08:29:38 -08:00
dependabot[bot]
72d20d5745 chore(deps): bump sysinfo from 0.27.7 to 0.28.0
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.27.7 to 0.28.0.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 08:29:10 -08:00
dependabot[bot]
38686a1167 chore(deps): bump clap from 4.1.4 to 4.1.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.4 to 4.1.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.1.4...v4.1.6)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 08:28:25 -08:00
LGUG2Z
d9648ddd0c fix(readme): correct whkdrc locations in quickstart 2023-02-19 09:05:15 -08:00
LGUG2Z
6d01e53ef3 chore(release): v0.1.15 2023-02-19 07:57:31 -08:00
LGUG2Z
f87d4d520b refactor(ahk): merge ahk v1/v2 exe env vars 2023-02-19 07:53:43 -08:00
LGUG2Z
0c8eceb0c4 docs(readme): general update + quickstart w/ whkd 2023-02-19 07:53:43 -08:00
LGUG2Z
afde7a3fb5 feat(config): add pwsh cfgen + ps1 file watcher
This commit adds a command to generate application-specific
configuration in the format of a PowerShell ps1 file, as well as the
ability to automatically launch a komorebi.ps1 configuration file on
startup.

If a komorebi.ps1 file is found and launched at startup, the
watch-configuration command will watch and hot reload this file when any
changes are made.

A sample komorebi.ps1 file has been added to the root of the repository,
along with a sample whkdrc file, showing how the two can be used
together to replace AHK.

re #339
2023-02-19 07:53:43 -08:00
LGUG2Z
b89e5eafd2 feat(wm): add internal transparent/opaque window fns 2023-02-16 14:37:08 -08:00
LGUG2Z
8adff69b81 feat(wm): add more named-workspace cmd variants 2023-02-16 12:24:42 -08:00
LGUG2Z
a23019eccf fix(wm): handle regression switching from empty ws 2023-02-14 18:54:16 -08:00
dependabot[bot]
7bd2ff4087 chore(deps): bump vedantmgoyal2009/winget-releaser from 1 to 2
Bumps [vedantmgoyal2009/winget-releaser](https://github.com/vedantmgoyal2009/winget-releaser) from 1 to 2.
- [Release notes](https://github.com/vedantmgoyal2009/winget-releaser/releases)
- [Commits](https://github.com/vedantmgoyal2009/winget-releaser/compare/v1...v2)

---
updated-dependencies:
- dependency-name: vedantmgoyal2009/winget-releaser
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 08:28:02 -08:00
dependabot[bot]
3cc0e5d4c4 chore(deps): bump ctrlc from 3.2.4 to 3.2.5
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/commits/3.2.5)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 07:52:11 -08:00
dependabot[bot]
4c35f47bc4 chore(deps): bump serde_json from 1.0.92 to 1.0.93
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.92 to 1.0.93.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.92...v1.0.93)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 07:35:31 -08:00
LGUG2Z
6b918dae7f feat(wm): add named workspace commands
This commit introduces three new commands, ensure-named-workspaces,
named-workspace-rule, and focus-named-workspace, which email to reduce
the configuration complexity by allowing users to refer to workspace
names instead of monitor and workspace indices.
2023-02-12 15:18:16 -08:00
LGUG2Z
bda6054044 perf(border): reduce redraws on workspace change 2023-02-11 16:07:53 -08:00
LGUG2Z
80c98596dd feat(wm): add "cloak" window-hiding-behaviour
This commit adds cloaking as a window-hiding-behaviour option.

https://devblogs.microsoft.com/oldnewthing/20200302-00/?p=103507 for
more information on cloaking.

Cloaking is the same mechanism used by the native Virtual Desktops
feature by the Windows team, however it is deliberately hidden from the
public Windows API.

GitHub user Ciantic's VirtualDesktopAccessor crate documents the private
IApplicationView COM interface which contains the hidden and
undocumented SetCloak method, which can be used to cloak and uncloak
application windows by their HWNDs.

With some help from Ciantic and manual exploration to determine the
correct flags values to use, komorebi is now able to use the cloaking
mechanism when switching workspaces, which results in significantly
higher reliability and significantly less jank on workspace transitions.
komorebi's use of this cloaking mechanism also retains the flexibility
of per-monitor workspaces that users have come to know and enjoy.

This has only been tested on Windows 11, it is not yet known if calling
the SetCloak function in IApplicationWindow will cause crashes on
Windows 10.
2023-02-11 15:14:20 -08:00
dependabot[bot]
89d1924736 chore(deps): bump clap from 4.1.1 to 4.1.4
Bumps [clap](https://github.com/clap-rs/clap) from 4.1.1 to 4.1.4.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.1.1...v4.1.4)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 08:21:26 -08:00
dependabot[bot]
5d6bc49ca9 chore(deps): bump heck from 0.4.0 to 0.4.1
Bumps [heck](https://github.com/withoutboats/heck) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/withoutboats/heck/releases)
- [Changelog](https://github.com/withoutboats/heck/blob/master/CHANGELOG.md)
- [Commits](https://github.com/withoutboats/heck/commits)

---
updated-dependencies:
- dependency-name: heck
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 08:19:50 -08:00
dependabot[bot]
c299326a27 chore(deps): bump proc-macro2 from 1.0.50 to 1.0.51
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.50 to 1.0.51.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.50...1.0.51)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 07:29:53 -08:00
dependabot[bot]
ec63b3e8bc chore(deps): bump os_info from 3.5.1 to 3.6.0
Bumps [os_info](https://github.com/stanislav-tkach/os_info) from 3.5.1 to 3.6.0.
- [Release notes](https://github.com/stanislav-tkach/os_info/releases)
- [Changelog](https://github.com/stanislav-tkach/os_info/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stanislav-tkach/os_info/compare/v3.5.1...v3.6.0)

---
updated-dependencies:
- dependency-name: os_info
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 07:05:42 -08:00
dependabot[bot]
d592889dbf chore(deps): bump serde_json from 1.0.91 to 1.0.92
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.91 to 1.0.92.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.91...v1.0.92)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 07:05:18 -08:00
dependabot[bot]
04a5b3e669 chore(deps): bump sysinfo from 0.27.6 to 0.27.7
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.27.6 to 0.27.7.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 06:27:42 -08:00
dependabot[bot]
49dcdf806a chore(deps): bump proc-macro2 from 1.0.49 to 1.0.50
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.49 to 1.0.50.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.49...1.0.50)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 06:26:49 -08:00
dependabot[bot]
0463a28d3d chore(deps): bump which from 4.3.0 to 4.4.0
Bumps [which](https://github.com/harryfei/which-rs) from 4.3.0 to 4.4.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.3.0...4.4.0)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 06:26:35 -08:00
dependabot[bot]
dc31ce227a chore(deps): bump serde_yaml from 0.9.16 to 0.9.17
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.16 to 0.9.17.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.16...0.9.17)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-23 06:26:07 -08:00
LGUG2Z
51f1aa7ede fix(wm): ensure all monitors are tiled on startup 2023-01-22 08:47:07 -08:00
LGUG2Z
86b4d239e7 fix(wm): allow focusing monitor with empty ws
This commit ensures that when the focus is changed to a monitor with an
empty workspace with the focus-monitor or cycle-monitor commands,
subsequent commands such as focus-workspace will operate successfully on
the chosen monitor.

fix #148
2023-01-21 18:34:30 -08:00
LGUG2Z
925f3bd87b feat(wm): add optional alt focus hack
This commit adds an optional focusing hack using simulated ALT key
presses to ensure that focus changes always succeed. As noted in the
documentation for LockSetForegroundWindow, the system automatically
enables calls to SetForegroundWindow if the user presses the ALT key.
2023-01-21 13:29:10 -08:00
LGUG2Z
f8120f6b11 fix(wm): correct logic for foreground access retry 2023-01-21 08:01:03 -08:00
LGUG2Z
67e0914e1e docs(readme): update workflow status badge 2023-01-20 17:14:14 -08:00
dependabot[bot]
a6e0fa2ca9 chore(deps): bump windows from 0.43.0 to 0.44.0
Bumps [windows](https://github.com/microsoft/windows-rs) from 0.43.0 to 0.44.0.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/compare/0.43.0...0.44.0)

---
updated-dependencies:
- dependency-name: windows
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-19 08:32:22 -08:00
dependabot[bot]
03fb786183 chore(deps): bump clap from 4.0.32 to 4.1.1
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.32 to 4.1.1.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.32...v4.1.1)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 06:19:39 -08:00
dependabot[bot]
0aab892269 chore(deps): bump sysinfo from 0.27.3 to 0.27.6
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.27.3 to 0.27.6.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 06:18:51 -08:00
dependabot[bot]
9561c0fba0 chore(deps): bump sysinfo from 0.27.2 to 0.27.3
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.27.2 to 0.27.3.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-16 04:17:02 -08:00
LGUG2Z
acc119a529 docs(readme): specify ahk1.1 requirement for sample cfg 2023-01-16 04:15:59 -08:00
LGUG2Z
fd351b6a01 fix(wm): add bounds checks on max window creation
This commit ensures that the bounds of the resize_dimensions member on a
workspace are checked before attempting removal when maximizing a window
using the toggle-maximize command or cycle-focus commands.

fix #331
2023-01-04 07:19:09 -08:00
dependabot[bot]
499a960f4c chore(deps): bump serde from 1.0.151 to 1.0.152
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.151 to 1.0.152.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.151...v1.0.152)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:20:27 -08:00
dependabot[bot]
6957af3196 chore(deps): bump sysinfo from 0.27.0 to 0.27.2
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.27.0 to 0.27.2.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:20:11 -08:00
dependabot[bot]
3f1348e5b9 chore(deps): bump clap from 4.0.29 to 4.0.32
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.29 to 4.0.32.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.29...v4.0.32)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:19:56 -08:00
dependabot[bot]
54b4b37836 chore(deps): bump syn from 1.0.105 to 1.0.107
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.105 to 1.0.107.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.105...1.0.107)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:19:40 -08:00
dependabot[bot]
93223d2d92 chore(deps): bump quote from 1.0.21 to 1.0.23
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.21 to 1.0.23.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.21...1.0.23)

---
updated-dependencies:
- dependency-name: quote
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-02 07:19:22 -08:00
LGUG2Z
99af0f8f7b fix(wm): make force-focus command read wm state
This commit ensures that the force-focus command reads the window
manager state to get the coordinates of the window that should be
currently focused, places the cursor in the middle of that rect, and
then simulates a left-click.
2022-12-21 15:36:55 -08:00
LGUG2Z
4ee4d199a0 feat(wm): add new cmd to set active border offset
This commit introduces a new command, active-window-border-offset, which
allows the user to offset the starting position of the active window
border, thereby allowing for thicker or thinner active window borders,
when used in conjunction with the active-window-border-width command.

resolve #232
2022-12-19 19:01:46 -08:00
dependabot[bot]
00477e2696 chore(deps): bump serde from 1.0.150 to 1.0.151
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.150 to 1.0.151.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.150...v1.0.151)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 09:07:26 -08:00
dependabot[bot]
fbb7f70b15 chore(deps): bump proc-macro2 from 1.0.47 to 1.0.49
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.47 to 1.0.49.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.47...1.0.49)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 09:06:39 -08:00
dependabot[bot]
71b6a0eeea chore(deps): bump serde_yaml from 0.9.14 to 0.9.16
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.14 to 0.9.16.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.14...0.9.16)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 09:05:51 -08:00
dependabot[bot]
5e5323e696 chore(deps): bump serde_json from 1.0.89 to 1.0.91
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.89 to 1.0.91.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.89...v1.0.91)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 09:05:12 -08:00
dependabot[bot]
13bf9e64da chore(deps): bump paste from 1.0.9 to 1.0.11
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.9 to 1.0.11.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.9...1.0.11)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-19 08:59:40 -08:00
LGUG2Z
613d69a737 feat(wm): add force-focus command
On rare occasions and usually with Firefox, the desired application will
fail to be focused with the regular focus commands. This commit
introduces a new command, force-focus, which can be used to simulate a
left-click on a window that has failed to take focus, since this is what
I have to do to work around the issue anyway.
2022-12-15 17:08:25 -08:00
dependabot[bot]
ca09b9b300 chore(deps): bump serde from 1.0.149 to 1.0.150
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.149 to 1.0.150.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.149...v1.0.150)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-12 08:03:06 -08:00
dependabot[bot]
fa87a8ca88 chore(deps): bump sysinfo from 0.26.8 to 0.27.0
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.26.8 to 0.27.0.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-12 08:02:52 -08:00
dependabot[bot]
7c25f2c2e4 chore(deps): bump ctrlc from 3.2.3 to 3.2.4
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.2.3 to 3.2.4.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/commits)

---
updated-dependencies:
- dependency-name: ctrlc
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-12 08:01:31 -08:00
LGUG2Z
e764dad7a4 feat(wm): make active border with configurable
This commit introduces a new command which lets the user set a custom
width value for the active window border when it is enabled.

Unfortunately a little more width is required when working with rounded
windows on Windows 11 to fill the gap left by the rounding. The default
width remains set at 20.

re #232
2022-12-09 18:58:02 -08:00
LGUG2Z
a2bd277620 docs(readme): add section for example config 2022-12-06 19:02:30 -08:00
LGUG2Z
97423fc8e9 feat(wm): move/send container to ws by direction
This commit introduces two new commands which will allow the user to
move or send the currently focused window to either the next or previous
workspace depending on the cycle direction.

re #297
2022-12-06 18:54:49 -08:00
LGUG2Z
3b0830e511 fix(wm): allow cross-monitor focus w/ monocle+max
This commit removes the focused window monocle/maximized check when
trying to focus a container in a direction that requires the focus to
cross a monitor boundary.

Unfortunately, trying to remove the same check from the move command
results in undesired behaviour  (the wrong window gets moved, the state
gets funny on both the origin and the destination monitor)
2022-12-06 18:29:31 -08:00
dependabot[bot]
f5c9008287 chore(deps): bump serde from 1.0.148 to 1.0.149
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.148 to 1.0.149.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.148...v1.0.149)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 11:26:34 -08:00
dependabot[bot]
0f7d164550 chore(deps): bump clap from 4.0.27 to 4.0.29
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.27 to 4.0.29.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.27...v4.0.29)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 11:26:28 -08:00
dependabot[bot]
22b8029fb4 chore(deps): bump syn from 1.0.104 to 1.0.105
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.104 to 1.0.105.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.104...1.0.105)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 11:26:20 -08:00
Anurag Rao
46f2aad674 Fixed remove gaps command 2022-11-29 05:22:48 -08:00
dependabot[bot]
6d9f51e645 chore(deps): bump serde from 1.0.147 to 1.0.148
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.147 to 1.0.148.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.147...v1.0.148)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 09:32:39 -08:00
dependabot[bot]
ec0dea588d chore(deps): bump sysinfo from 0.26.7 to 0.26.8
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.26.7 to 0.26.8.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 09:32:28 -08:00
dependabot[bot]
24f838e83e chore(deps): bump clap from 4.0.23 to 4.0.27
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.23 to 4.0.27.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.23...v4.0.27)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 09:32:09 -08:00
dependabot[bot]
13114724b7 chore(deps): bump serde_json from 1.0.87 to 1.0.89
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.87 to 1.0.89.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.87...v1.0.89)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 09:31:54 -08:00
dependabot[bot]
267c24bc75 chore(deps): bump crossbeam-utils from 0.8.12 to 0.8.14
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.12 to 0.8.14.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.12...crossbeam-utils-0.8.14)

---
updated-dependencies:
- dependency-name: crossbeam-utils
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-28 09:31:44 -08:00
dependabot[bot]
57b1bc1414 chore(deps): bump miow from 0.4.0 to 0.5.0
Bumps [miow](https://github.com/yoshuawuyts/miow) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/yoshuawuyts/miow/releases)
- [Changelog](https://github.com/yoshuawuyts/miow/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yoshuawuyts/miow/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: miow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 08:10:46 -08:00
dependabot[bot]
22dcf15129 chore(deps): bump clap from 4.0.22 to 4.0.23
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.22 to 4.0.23.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.22...v4.0.23)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-14 08:09:57 -08:00
LGUG2Z
e1634d2a32 docs(readme): add section on youtube livestreams 2022-11-13 20:31:29 -08:00
LGUG2Z
37edbcfebc refactor(clippy): apply various lint fixes 2022-11-13 15:31:19 -08:00
LGUG2Z
b010215318 feat(wm): add per-monitor work area offsets
This commit introduces per-monitor work area offsets which will always
take precedence over global work area offsets.
2022-11-12 18:23:24 -08:00
LGUG2Z
a48715f15a refactor(core): make cfgen struct members public 2022-11-10 17:16:32 -08:00
LGUG2Z
6308414129 chore(release): v0.1.14 2022-11-09 13:38:57 -08:00
LGUG2Z
035e77bd25 fix(wm): don't reconcile monitors during drag
This commit ensures that monitor reconciliation doesn't get triggered
eagerly when a cross-monitor mouse drag/move of a window is taking
place.
2022-11-09 06:25:21 -08:00
dependabot[bot]
7b98b563eb chore(deps): bump clap from 4.0.18 to 4.0.20
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.18 to 4.0.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.18...v4.0.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-07 09:51:04 -08:00
LGUG2Z
5fda4a39b8 docs(readme): convert badges to html 2022-11-03 09:34:25 -07:00
LGUG2Z
3ad0ae6aca feat(wm): introduce lazy monitor cache
This commit introduces a lazy monitor cache that only gets populated
with a monitor has been disconnected, before the monitor is removed from
the state. If and when the same monitor is reconnected and identified by
its size on the virtual screen, the cached monitor state will be used to
repopulate layout options, avoiding a potentially expensive full
configuration reload.

re #275
2022-10-31 12:38:59 -07:00
LGUG2Z
2575ca2da4 feat(wm): add monitor index preference cmd
This commit adds a new komorebic command, monitor-index-preference,
which allows the user to set the index preference within the VecDeque of
monitors based on the "size" of a display.

This works as the size Rect identifies a unique display on the greater
virtual screen and persists across display connections and
disconnections unless the user deliberately changes the positioning of
the display monitor on the virtual screen.

When a new monitor is added to the state, the monitor preferences will
be checked, and if a preference exists, the new monitor will be inserted
at that index, otherwise, it will be pushed to the back of the VecDeque.

resolve #275
2022-10-31 12:38:59 -07:00
LGUG2Z
37f1a163cc fix(wm): match display name / avoid id volatility
This commit replaces all usages of MONITORINFO with MONITORINFOEX in
order to retrieve a name for each connected display device.

This display device name makes for easier deduping during monitor
reconciliation, so that matching display monitor names can simply have
their hmonitor id updated instead of trying to figure out which id
corresponds to which monitor by looking at the windows currently visible
on each.

fix #267
2022-10-31 12:38:59 -07:00
LGUG2Z
91c532d9b1 fix(wm): listen to settingchange + devicechange
This commit adds listeners on two more events, WM_SETTINGCHANGE and
WM_DEVICECHANGE, in the hope of more reliably catching monitor
attachments and detachments based on info in an SO answer.

fix #267
2022-10-31 12:38:59 -07:00
LGUG2Z
75d72522a2 fix(wm): listen to displaychange w/ hidden hwnd
This commit removes the previous polling strategy on ObjectCreate events
and uses a hidden window to listen to WM_DISPLAYCHANGE.

Unfortunately, as all monitors change HMONITOR values on monitor
attach/detach, even if the monitor remains attached, the only real
choice we have when a monitor which previously held windows is detached
is to read the entire monitor and workspace state again, as we do when
we initialise the window manager for the first time.

Since it's possible that the "wrong" monitor in the state has its
HMONITOR value updated, we also have to load the configuration again.

This commit deprecates WindowManagerEvent::MonitorPoll.

fix #267
2022-10-31 12:38:59 -07:00
dependabot[bot]
438bfc86ff chore(deps): bump windows from 0.42.0 to 0.43.0
Bumps [windows](https://github.com/microsoft/windows-rs) from 0.42.0 to 0.43.0.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/compare/0.42.0...0.43.0)

---
updated-dependencies:
- dependency-name: windows
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 09:23:01 -07:00
dependabot[bot]
ca86418e3c chore(deps): bump sysinfo from 0.26.4 to 0.26.7
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.26.4 to 0.26.7.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 09:01:45 -07:00
dependabot[bot]
d8c76b97a9 chore(deps): bump serde from 1.0.145 to 1.0.147
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.145 to 1.0.147.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.145...v1.0.147)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-31 09:01:19 -07:00
dependabot[bot]
a4be1b95e0 chore(deps): bump net2 from 0.2.37 to 0.2.38
Bumps [net2](https://github.com/deprecrated/net2-rs) from 0.2.37 to 0.2.38.
- [Release notes](https://github.com/deprecrated/net2-rs/releases)
- [Commits](https://github.com/deprecrated/net2-rs/compare/0.2.37...0.2.38)

---
updated-dependencies:
- dependency-name: net2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 08:38:58 -07:00
dependabot[bot]
6e666bc4d8 chore(deps): bump serde_json from 1.0.86 to 1.0.87
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.86 to 1.0.87.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.86...v1.0.87)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 08:38:48 -07:00
dependabot[bot]
857b3c9ccf chore(deps): bump syn from 1.0.102 to 1.0.103
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.102 to 1.0.103.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.102...1.0.103)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 08:38:36 -07:00
dependabot[bot]
9b916c0c21 chore(deps): bump clap from 4.0.15 to 4.0.18
Bumps [clap](https://github.com/clap-rs/clap) from 4.0.15 to 4.0.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.0.15...v4.0.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 08:38:22 -07:00
dependabot[bot]
b642eddb96 chore(deps): bump serde_yaml from 0.9.13 to 0.9.14
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.13 to 0.9.14.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.13...0.9.14)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-24 08:37:55 -07:00
LGUG2Z
1229c65580 fix(ahk): prefer runwait for ahk lib wrappers
This commit ensures that the generated AHK library for komorebic uses
RunWait instead of Run, as the latter is asynchronous and can result in
an unexpected order of calls when used in a komorebi.ahk configuration
file.

re #269
2022-10-21 15:24:31 -07:00
LGUG2Z
b25cc1c8bf fix(wm): reset foreground access when calls fail 2022-10-21 14:10:12 -07:00
LGUG2Z
e02ddd47cc fix(wm): eagerly set new active border position
This commit adds SocketMessage::FocusWindow as a target to update the
border position on at the end of the command handler. There are some
occasions where the EVENT_SYSTEM_FOREGROUND notification isn't being
sent (on newer versions of Win11, I think), so we can't count on the
border being updated when that event is received by the event handler.
2022-10-21 12:13:06 -07:00
LGUG2Z
e20b4aabc3 fix(clap): address deprecations 2022-10-20 14:21:27 -07:00
dependabot[bot]
acaee5595d chore(deps): bump clap from 3.2.22 to 4.0.15
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.22 to 4.0.15.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.22...v4.0.15)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-20 14:21:27 -07:00
dependabot[bot]
083a142597 chore(deps): bump proc-macro2 from 1.0.46 to 1.0.47
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.46 to 1.0.47.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.46...1.0.47)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-17 09:17:02 -07:00
Max Bane
1633a5ebc2 fix(config): call ActiveWindowBorderColour correctly
The sample config only provided three of the four required arguments in
the example call to ActiveWindowBorderColour. Uncommenting the line as
it was would raise an error from AuthoHotKey.
2022-10-14 11:40:17 -07:00
Yusuf007R
352c010021 feat(wm): manage return bool of PostMessage + update ahk library
this commit makes a small refactor to the way PostMessageW is used so now be able to handle the
returned bool, as well as adding a custom error message to WindowsApi::close_window and updating the
ahk generated library

#259
2022-10-13 01:08:35 -07:00
Yusuf007R
33965f92ad feat(wm): add close and minimize cmd
this commit adds the ability to close and minimize the current focused window via SocketMessages and
komorebic commands

resolve #189
2022-10-13 01:08:35 -07:00
Yusuf007R
635272fc10 feat(wm): add max number of attempts to set foreground
this commits adds a max number of attempts (5) to the call of set_foreground_window inside
Window:Focus
2022-10-13 01:06:58 -07:00
dependabot[bot]
5354e9c7a4 chore(deps): bump tracing-subscriber from 0.3.15 to 0.3.16
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.15 to 0.3.16.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.15...tracing-subscriber-0.3.16)

---
updated-dependencies:
- dependency-name: tracing-subscriber
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 08:27:26 -07:00
dependabot[bot]
e35164f106 chore(deps): bump tracing from 0.1.36 to 0.1.37
Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.36 to 0.1.37.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.36...tracing-0.1.37)

---
updated-dependencies:
- dependency-name: tracing
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 08:27:16 -07:00
dependabot[bot]
a637eefd82 chore(deps): bump syn from 1.0.101 to 1.0.102
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.101 to 1.0.102.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.101...1.0.102)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 08:27:01 -07:00
dependabot[bot]
db361f36db chore(deps): bump serde_json from 1.0.85 to 1.0.86
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.85 to 1.0.86.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.85...v1.0.86)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-10 08:26:43 -07:00
LGUG2Z
577fa0a97f fix(wm): retry foreground calls on focus change
For the past few weeks since upgrading the windows-rs crate I've seen
sporadic failures when calling SetForegroundWindow which require a full
restart to get the wm working as expected again. Adding in a retry loop
here seems to help when the issue comes up for me on Windows 11.
2022-10-07 06:57:37 -07:00
dependabot[bot]
f8ada73739 chore(deps): bump proc-macro2 from 1.0.44 to 1.0.46
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.44 to 1.0.46.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.44...1.0.46)

---
updated-dependencies:
- dependency-name: proc-macro2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 07:58:29 -07:00
dependabot[bot]
ffcc2f71d1 chore(deps): bump crossbeam-utils from 0.8.11 to 0.8.12
Bumps [crossbeam-utils](https://github.com/crossbeam-rs/crossbeam) from 0.8.11 to 0.8.12.
- [Release notes](https://github.com/crossbeam-rs/crossbeam/releases)
- [Changelog](https://github.com/crossbeam-rs/crossbeam/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossbeam-rs/crossbeam/compare/crossbeam-utils-0.8.11...crossbeam-utils-0.8.12)

---
updated-dependencies:
- dependency-name: crossbeam-utils
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 07:58:10 -07:00
dependabot[bot]
ed79793002 chore(deps): bump schemars from 0.8.10 to 0.8.11
Bumps [schemars](https://github.com/GREsau/schemars) from 0.8.10 to 0.8.11.
- [Release notes](https://github.com/GREsau/schemars/releases)
- [Changelog](https://github.com/GREsau/schemars/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GREsau/schemars/compare/v0.8.10...v0.8.11)

---
updated-dependencies:
- dependency-name: schemars
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-03 07:57:52 -07:00
sitiom
afb819a383 ci(winget): update workflow 2022-09-29 08:16:10 -07:00
LGUG2Z
3438818999 chore(deps): cargo update 2022-09-27 09:54:34 -07:00
LGUG2Z
611e4cc4a8 chore(deps): bump windows from 0.40 to 0.42 2022-09-27 09:53:43 -07:00
dependabot[bot]
27490de0d1 chore(deps): bump windows from 0.39.0 to 0.40.0
Bumps [windows](https://github.com/microsoft/windows-rs) from 0.39.0 to 0.40.0.
- [Release notes](https://github.com/microsoft/windows-rs/releases)
- [Commits](https://github.com/microsoft/windows-rs/compare/0.39.0...0.40.0)

---
updated-dependencies:
- dependency-name: windows
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-20 08:28:02 -07:00
dependabot[bot]
5112a8f39d chore(deps): bump syn from 1.0.99 to 1.0.100
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.99 to 1.0.100.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.99...1.0.100)

---
updated-dependencies:
- dependency-name: syn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-19 07:37:14 -07:00
LGUG2Z
c3f77ef4f8 chore(deps): cargo update 2022-09-18 08:37:24 -07:00
LGUG2Z
33520a46b5 fix(cli): open resp sock before sending query
This commit ensures that a response socket is opened before sending a
query request from komorebic to komorebi. Additionally, I have taken
this opportunity to ensure that all socket files are created in
DATA_DIR.

fix #218
2022-09-18 08:30:09 -07:00
LGUG2Z
0903be7931 fix(cli): pass correct tcp flag to start cmd 2022-09-13 15:53:57 -07:00
dependabot[bot]
e78e6b1382 chore(deps): bump paste from 1.0.8 to 1.0.9
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.8 to 1.0.9.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.8...1.0.9)

---
updated-dependencies:
- dependency-name: paste
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 13:13:24 -07:00
dependabot[bot]
adafa32488 chore(deps): bump sysinfo from 0.26.0 to 0.26.2
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.26.0 to 0.26.2.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 13:13:14 -07:00
dependabot[bot]
329b3052a4 chore(deps): bump clap from 3.2.17 to 3.2.20
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.17 to 3.2.20.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.20/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.17...v3.2.20)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 13:12:54 -07:00
dependabot[bot]
b25662fea2 chore(deps): bump which from 4.2.5 to 4.3.0
Bumps [which](https://github.com/harryfei/which-rs) from 4.2.5 to 4.3.0.
- [Release notes](https://github.com/harryfei/which-rs/releases)
- [Commits](https://github.com/harryfei/which-rs/compare/4.2.5...4.3.0)

---
updated-dependencies:
- dependency-name: which
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 13:12:34 -07:00
dependabot[bot]
edc9b0cd3d chore(deps): bump serde_yaml from 0.9.10 to 0.9.11
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.10 to 0.9.11.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.10...0.9.11)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-05 13:12:01 -07:00
sitiom
a43eb8fbf5 docs(readme): add winget installation reference 2022-09-05 07:47:58 -07:00
Ryan Caezar Itang
8b0f1d007f ci(windows): add winget releaser workflow 2022-08-30 12:46:06 -07:00
dependabot[bot]
83a502f199 chore(deps): bump sysinfo from 0.25.3 to 0.26.0
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.25.3 to 0.26.0.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-29 07:50:20 -07:00
LGUG2Z
5006aa9009 fix(wm): give focus priority to monocle containers
This commit ensures that monocle containers are given priority when
handling WindowManagerEvent::FocusChange.

This is especially important when switching workspaces to ensure that
the keyboard input focus stays with the monocle container's focused
window when returning to a workspace with a monocle container activated.

fix #219
2022-08-28 08:09:10 -07:00
LGUG2Z
e4a8117a94 docs(readme): add link to blog by omochice 2022-08-27 19:08:12 -07:00
LGUG2Z
748c389b34 chore(release): v0.1.13 2022-08-27 15:12:23 -07:00
LGUG2Z
26a18adeb4 fix(wm): restore monocle border on ws change
This commit ensures that when navigating away from and then back to a
workspace with a monocle window container, that the monocle window
container will be properly focused when navigating back, including
having the focus of the active window border.

fix #219
2022-08-27 15:00:23 -07:00
LGUG2Z
5d094f601f fix(wm): enforce valid hwnd check for border fns
This commit ensures that the active window border has non-zero HWND
before attempting to either hide it or set the border position. This is
required as the border is only initialized when a komorebic command is
received, meaning that the default value of 0 will never change if a
user decides to use komorebi without the active window border.

Most notably this commit fixes an issue where users who did not have the
active window border enabled would not be able to move away from an
empty workspace using a komorebic command.

fix #217
2022-08-27 14:56:47 -07:00
LGUG2Z
5a0ba4cdbb fix(wm): detach thread inputs explicitly
This commit ensures that any calls to AttachThreadInput which are used
to allow the focusing or raising of a window are paired with a closing
call to detach the thread input.

Although undocumented, it seems that when attaching the input thread of
a window to an admin/sudo process, this prevents that window from
handling inputs from any unelevated processes (including regular
keyboard and mouse inputs), until the input thread is detached.

fix #86
2022-08-27 13:20:37 -07:00
LGUG2Z
7c41460b14 fix(wm): remove mstsc.exe from wsl2 ui proc list
This commit removes mstsc.exe from WSL2_UI_PROCESSES. Recent changes to
WSLg unfortunately mean that even with this exe being included in the
override list, WSLg windows once again no longer tile correctly. On top
of that, mstsc.exe is also used for traditional Windows RDP connections,
so leaving this in the override list results in ghost window tiles for
users connecting to other machines via RDP.

Users who wish to keep mstsc.exe included in WSL2_UI_PROCESSES are
welcome to maintain a fork of komorebi.

My official recommendation for users wishing to run Linux GUI
applications from WSL on Windows is to use VcXsrv, which is fully
compatible with komorebi, and generall just a very mature, stable, tried
and tested piece of software.

fix #216
2022-08-23 07:06:37 -07:00
dependabot[bot]
d34a561753 chore(deps): bump serde_json from 1.0.83 to 1.0.85
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.83 to 1.0.85.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.83...v1.0.85)

---
updated-dependencies:
- dependency-name: serde_json
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 11:57:57 -07:00
dependabot[bot]
04146a3ce9 chore(deps): bump serde_yaml from 0.9.9 to 0.9.10
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.9 to 0.9.10.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.9...0.9.10)

---
updated-dependencies:
- dependency-name: serde_yaml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 11:57:46 -07:00
dependabot[bot]
09a544b45b chore(deps): bump serde from 1.0.143 to 1.0.144
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.143 to 1.0.144.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.143...v1.0.144)

---
updated-dependencies:
- dependency-name: serde
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 11:57:26 -07:00
dependabot[bot]
0e1ad164d4 chore(deps): bump sysinfo from 0.25.2 to 0.25.3
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.25.2 to 0.25.3.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-22 11:57:11 -07:00
LGUG2Z
cec8b04ffd docs(readme): add charitable donations section 2022-08-20 08:29:03 -07:00
LGUG2Z
f4b3d568ee fix(wm): add bounds checking in move cmd
This commit ensures that when there is only one container on the target
workspace in a cross-monitor move, meaning that there won't be one
swapped back, we only decrement the focused container index on the
origin workspace if there is a focused container at an index greater
than 0.

fix #209
2022-08-18 14:51:39 -07:00
LGUG2Z
d3dc193d29 feat(tcp): add listener + export socket schema
This commit adds a TCP listener that can be optionally exposed on a port
provided by the user using the --tcp-port flag. If the flag is not
provided, the TCP listener will not be started.

Client state is tracked using the connecting address, and clients are
purged if they send unrecognised messages.

A JSON Schema of the SocketMessage enum can be exported via komorebic
and be used to generate type definitions in various programming
languages.

This commit also makes some improvements to the handling of 'komorebic
start'.

The previous backoff approach was not working as once the Windows API
denies access to the process for any call, no amount of retries with the
same process id will result in approval.

Therefore, 'komorebic start' now checks if the process has been started,
and if it hasn't (ie. it has errored out because of an access denied
error), it will continue to restart the process until all the komorebi
startup calls to the Windows API succeed.

resolve #176
2022-08-15 09:35:08 -07:00
LGUG2Z
441bfce053 fix(wm): update border on hide events 2022-08-13 17:41:28 -07:00
LGUG2Z
458d1ef80a feat(wm): add promote-focus command
This commit adds a promote-focus command to complement the promote
command.

resolve #203
2022-08-13 17:10:55 -07:00
LGUG2Z
be5945c64b fix(wm): create active border only on command 2022-08-13 16:36:23 -07:00
LGUG2Z
38ce38d65c fix(wm): improve startup reliability
This commit wraps calls to the Windows API which may intermittently fail
in backoff blocks, reducing the potential of early exits from errors
returned by the Windows API before the tiling has even started.
Hopefully this makes calls to 'komorebic start' more relible for use at
login time.
2022-08-13 14:40:10 -07:00
LGUG2Z
6ed52c9387 fix(wm): reduce floating window border jank
This commit reduces some of the jank when the active border window deals
with windows that have been floated by the wm.

- The border on a floated window is always on top of all other windows,
  just like the floated window itself
- When a floated window is moved by the mouse, it retains its border
- When a floated window loses and then regains focus via mouse
  interactions, it regains its border

Note that now border changes are handled afer the main match block in
process_event.rs, early returns should be avoided unless absolutely
necessary, as this will prevent the border state from being updated
until the next event is received.
2022-08-13 14:20:57 -07:00
LGUG2Z
e466a17877 feat(wm): restrict border window to outline
This commit moves the border window drawing logic into the WNDPROC
callback and uses BeginPaint -> Rectangle -> Endpaint to draw a
rectangle around the outside of the window in a specific colour that is
not black, which is used as the transparency colour with
SetLayeredWindowAttributes.

All of this results in a non-filled border rect and a much nicer
experience for users who are using transparency or translucent effects
on their windows.

This commit also introduces an optional second active border colour when
the user is focused on a stack of windows. If this is not set, the
default colour for single windows will be used.

Finally, a bunch of small issues relating to the border window staying
drawn on the screen even when there are no active windows on a workspace
have been addressed.

resolve #201
2022-08-13 08:16:27 -07:00
LGUG2Z
f5def84010 fix(ahk): gen multi-word + multiple flags correctly 2022-08-12 13:04:01 -07:00
LGUG2Z
12473aa41c chore(editor): remove jetbrains iml file from repo 2022-08-12 11:21:51 -07:00
dependabot[bot]
33d1c0edbc chore(deps): bump powershell_script from 1.0.2 to 1.0.4
Bumps [powershell_script](https://github.com/cfsamson/powershell-script) from 1.0.2 to 1.0.4.
- [Release notes](https://github.com/cfsamson/powershell-script/releases)
- [Commits](https://github.com/cfsamson/powershell-script/commits)

---
updated-dependencies:
- dependency-name: powershell_script
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-12 11:19:31 -07:00
dependabot[bot]
8d346627d5 chore(deps): bump clap from 3.2.16 to 3.2.17
Bumps [clap](https://github.com/clap-rs/clap) from 3.2.16 to 3.2.17.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/v3.2.17/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v3.2.16...v3.2.17)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-12 11:19:21 -07:00
LGUG2Z
be83b4b5f2 docs(config): provide basic sample with lib + gen
This commit introduces a new komorebi.sample.ahk in the repository root,
as well as adding the latest generated versions of komorebic.lib.ahk and
komorebi.generated.ahk.

Pushing new users to use the AHK library by default will significantly
simplify the process of building a new configuration, and including the
application-specific configuration generated from the configuration
repository will result in a better first impression of komorebi where
more and more applications "just work".

This new sample is focused on setting a few sane configuration defaults,
and as few keybinds as possible, really just enough to allow the user to
switch focus and move windows around. This significantly reduces the
possibility of the first-time user accidentally triggering a command
that leaves them confused, frustrated and would probably end in them
killing the komorebi.exe proc from the task manager.

The new sample configuration will no longer be bundled with scoop
starting from the next release, which is also expected to introduce
support for installation via winget.

Instead, instructions have been added for users to download the latest
example configuration and generated libs from GitHub in the getting
started section.

resolve #62
2022-08-12 10:29:18 -07:00
LGUG2Z
f7ac1d0ece ci(artifacts): add msi to release artifacts
These changes to the GitHub actions workflows will include an MSI
installer in the artifacts that are uploaded at the end of each
successful build, and also attach an MSI installer to a release when the
job runs on a tag that creates a new release version.

re #152
2022-08-12 09:45:36 -07:00
LGUG2Z
2ba3ca4f31 build(msi): add configuration for cargo wix 2022-08-12 09:45:36 -07:00
45 changed files with 5651 additions and 2078 deletions

View File

@@ -32,6 +32,20 @@ OS Name: Microsoft Windows 11 Pro
OS Version: 10.0.22000 N/A Build 22000
```
**`komorebic check` Output**
Provide the output of `komorebic check`
For example:
```
No KOMOREBI_CONFIG_HOME detected, defaulting to C:\Users\LGUG2Z
Looking for configuration files in C:\Users\LGUG2Z
No komorebi configuration found in C:\Users\LGUG2Z
If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration
```
**Additional context**
Add any other context about the problem here.

View File

@@ -13,7 +13,7 @@ updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "monthly"
interval: "weekly"
assignees:
- "LGUG2Z"
commit-message:

View File

@@ -76,6 +76,10 @@ jobs:
- name: Run a full build
run: |
cargo build --locked --release --target ${{ matrix.target }}
- name: Create MSI installer
run: |
cargo install cargo-wix
cargo wix -p komorebi --nocapture -I .\wix\main.wxs --target x86_64-pc-windows-msvc
- name: Upload the built artifacts
uses: actions/upload-artifact@v3
with:
@@ -85,19 +89,39 @@ jobs:
target/${{ matrix.target }}/release/komorebic.exe
target/${{ matrix.target }}/release/komorebi.pdb
target/${{ matrix.target }}/release/komorebic.pdb
target/wix/komorebi-*.msi
retention-days: 7
# Release
- name: Generate changelog
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: |
if ! type kokai >/dev/null; then cargo install --locked kokai --force; fi
kokai release --no-emoji --add-links github:commits,issues --ref "$(git tag --points-at HEAD)" >"CHANGELOG.md"
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v3
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/v')
with:
version: latest
args: release --skip-validate --rm-dist --release-notes=CHANGELOG.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SCOOP_TOKEN: ${{ secrets.SCOOP_TOKEN }}
- name: Add MSI to release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/v')
with:
files: "target/wix/komorebi-*.msi"
winget:
name: Publish to WinGet
runs-on: windows-latest
needs: build
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: LGUG2Z.komorebi
token: ${{ secrets.WINGET_TOKEN }}

View File

@@ -35,11 +35,10 @@ archives:
name_template: "{{ .ProjectName }}-{{ .Version }}-{{ .Arch }}-{{ .Os }}"
files:
- LICENSE
- komorebi.sample.ahk
- CHANGELOG.md
checksum:
name_template: checksums.txt
changelog:
sort: asc
sort: asc

849
Cargo.lock generated

File diff suppressed because it is too large Load Diff

553
README.md
View File

@@ -2,15 +2,54 @@
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)
<p>
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/LGUG2Z/komorebi/.github/workflows/windows.yaml">
<img alt="GitHub" src="https://img.shields.io/github/license/LGUG2Z/komorebi">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/LGUG2Z/komorebi/total">
<img alt="GitHub commits since latest release (by date) for a branch" src="https://img.shields.io/github/commits-since/LGUG2Z/komorebi/latest">
<a href="https://discord.gg/mGkn66PHkx">
<img alt="Discord" src="https://img.shields.io/discord/898554690126630914">
</a>
<a href="https://github.com/sponsors/LGUG2Z">
<img alt="GitHub Sponsors" src="https://img.shields.io/github/sponsors/LGUG2Z">
</a>
<a href="https://notado.app/feeds/jado/software-development">
<img alt="Notado Feed" src="https://img.shields.io/badge/Notado-Subscribe-informational">
</a>
<a href="https://jeezy.substack.com">
<img alt="Substack Read" src="https://img.shields.io/badge/Substack-Read-orange">
</a>
<a href="https://twitter.com/LGUG2Z">
<img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/LGUG2Z">
</a>
</p>
![screenshot](https://user-images.githubusercontent.com/13164844/184027064-f5a6cec2-2865-4d65-a549-a1f1da589abf.png)
- [About](#about)
- [Charitable Donations](#charitable-donations)
- [GitHub Sponsors](#github-sponsors)
- [Demonstrations](#demonstrations)
- [Description](#description)
- [Design](#design)
- [Getting Started](#getting-started)
- [Quickstart](#quickstart)
- [GitHub Releases](#github-releases)
- [Building from Source](#building-from-source)
- [Running](#running)
- [Configuring](#configuring)
- [Common First-Time Tips](#common-first-time-tips)
- [Development](#development)
- [Logs and Debugging](#logs-and-debugging)
- [Restoring Windows](#restoring-windows)
- [Panics and Deadlocks](#panics-and-deadlocks)
- [Window Manager State and Integrations](#window-manager-state-and-integrations)
- [Window Manager Event Subscriptions](#window-manager-event-subscriptions)
- [Subscription Event Notification Schema](#subscription-event-notification-schema)
- [Communication over TCP](#communication-over-tcp)
- [Socket Message Schema](#socket-message-schema)
- [Appreciations](#appreciations)
## About
_komorebi_ is a tiling window manager that works as an extension to
@@ -21,6 +60,10 @@ _komorebi_ allows you to control application windows, virtual workspaces and dis
used with third-party software such as [AutoHotKey](https://github.com/Lexikos/AutoHotkey_L) to set user-defined
keyboard shortcuts.
_komorebi_ aims to make _as few modifications as possible_ to the operating system and desktop environment by default.
Users are free to make such modifications in their own configuration files for _komorebi_, but these will remain
opt-in and off-by-default for the foreseeable future.
Translations of this document can be found in the project wiki:
- [komorebi 中文用户指南](https://github.com/LGUG2Z/komorebi/wiki/README-zh) (by [@crosstyan](https://github.com/crosstyan))
@@ -29,12 +72,27 @@ There is a [Discord server](https://discord.gg/mGkn66PHkx) available for _komore
troubleshooting etc. If you have any specific feature requests or bugs to report, please create an issue in this
repository.
There is a [YouTube channel](https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg) where I livestream development
on _komorebi_. If you would like to be notified of upcoming livestreams please subscribe and turn on
notifications. Videos of previous livestreams are also made available in
a [dedicated playlist](https://www.youtube.com/playlist?list=PLllZnrEJu89Cpu4tMO8LAg1m6gWYWLSGJ).
Articles, blog posts, demos, and videos about _komorebi_ can be added to this list by PR:
- [Moving to Windows from Linux Pt 1](https://kvwu.io/posts/moving-to-windows/)
- [Windows 下的现代化平铺窗口管理器 komorebi](https://zhuanlan.zhihu.com/p/455064481)
- [komorebi を導入してみる](https://zenn.dev/omochice/articles/50f42a3df8f426)
## GitHub Sponsors Early Access
## Charitable Donations
_komorebi_ is a free and open-source project, and one that encourages you to make charitable donations if
you find the software to be useful and have the financial means.
I encourage you to make a charitable donation
to [Fresh Start Refugee](https://www.freshstartrefugee.org/donate) before
you consider sponsoring me on GitHub.
## GitHub Sponsors
[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
@@ -70,27 +128,19 @@ messages it receives on a dedicated socket.
_komorebic_ is a CLI that writes messages on _komorebi_'s socket.
_komorebi_ doesn't handle any keyboard or mouse inputs; a third party program (e.g. AutoHotKey) is needed in order to
translate keyboard and mouse events to _komorebic_ commands.
_komorebi_ doesn't handle any keyboard or mouse inputs; a third party program (e.g.
[whkd](https://github.com/LGUG2Z/whkd)) is needed in order to translate keyboard and mouse events to _komorebic_ commands.
This architecture, popularised by [_bspwm_](https://github.com/baskerville/bspwm) on Linux and
[_yabai_](https://github.com/koekeishiya/yabai) on macOS, is outlined as follows:
```
PROCESS SOCKET
ahk --------> komorebic <------> komorebi
PROCESS SOCKET
whkd/ahk --------> komorebic <------> komorebi
```
## Design
_komorebi_ is the successor to [_yatta_](https://github.com/LGUG2Z/yatta) and as such aims to build on the learnings
from that project.
While _yatta_ was primary an attempt to learn how to work with and call Windows APIs from Rust, while secondarily
implementing a minimal viable tiling window manager for my own needs (largely single monitor, single workspace),
_komorebi_ has been redesigned from the ground-up to support more complex features that have become standard in tiling
window managers on other platforms.
_komorebi_ holds a list of physical monitors.
A monitor is just a rectangle of the available work area which contains one or more virtual workspaces.
@@ -108,36 +158,75 @@ This means that:
## Getting Started
### Quickstart
Make sure that you have either the [Scoop Package Manager](https://scoop.sh) or WinGet installed, then run the following
commands at a PowerShell prompt.
```powershell
# if using scoop
scoop bucket add extras
scoop install whkd
scoop install komorebi
# if using winget
winget install LGUG2Z.whkd
winget install LGUG2Z.komorebi
# save the latest generated app-specific config tweaks and fixes to ~/komorebi.generated.ps1
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.generated.ps1 -OutFile $Env:USERPROFILE\komorebi.generated.ps1
# save the sample komorebi configuration file to ~/komorebi.ps1
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.sample.ps1 -OutFile $Env:USERPROFILE\komorebi.ps1
# ensure the ~/.config folder exists
mkdir $Env:USERPROFILE\.config -ea 0
# save the sample whkdrc file with key bindings to ~/.config/whkdrc
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/whkdrc.sample -OutFile $Env:USERPROFILE\.config\whkdrc
# start komorebi
komorebic start --await-configuration
```
Thanks to [@sitiom](https://github.com/sitiom) for getting _komorebi_ added to both the popular Scoop Extras bucket and
to WinGet.
You can watch a walkthrough video of this quickstart below on YouTube.
[![Watch the quickstart walkthrough video](https://img.youtube.com/vi/cBnLIwMtv8g/hqdefault.jpg)](https://www.youtube.com/watch?v=cBnLIwMtv8g)
#### Using Autohotkey
If you would like to use Autohotkey, please make sure you have AutoHotKey v2 installed.
Generally, users who opt for AHK will have specific needs that can only be addressed by the advanced functionality of AHK,
and so they are assumed to be able to craft their own configuration files.
If you would like to try out AHK, a simple sample configuration powered by `komorebic.lib.ahk` is provided as a starting
point.
```powershell
# save the latest generated komorebic library to ~/komorebic.lib.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebic.lib.ahk -OutFile $Env:USERPROFILE\komorebic.lib.ahk
# save the latest generated app-specific config tweaks and fixes to ~/komorebi.generated.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.generated.ahk -OutFile $Env:USERPROFILE\komorebi.generated.ahk
# save the sample komorebi configuration file to ~/komorebi.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.sample.ahk -OutFile $Env:USERPROFILE\komorebi.ahk
```
### GitHub Releases
Prebuilt binaries are available on the [releases page](https://github.com/LGUG2Z/komorebi/releases) in a `zip` archive.
Once downloaded, you will need to move the `komorebi.exe` and `komorebic.exe` binaries to a directory in your `Path` (
you can see these directories by running `$Env:Path.split(";")` at a PowerShell prompt).
Alternatively, you may add a new directory to your `Path`
using [`setx`](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/setx) or the Environment
Variables pop up in System Properties Advanced (which can be launched with `SystemPropertiesAdvanced.exe` at a
PowerShell prompt), and then move the binaries to that directory.
### Scoop
If you use the [Scoop](https://scoop.sh/) command line installer, you can run the following commands to install the
binaries from the latest GitHub Release:
```powershell
scoop bucket add extras
scoop install komorebi
```
If you install _komorebi_ using Scoop, the binaries will automatically be added to your `Path` and a command will be
shown for you to run in order to get started using the sample configuration file.
Thanks to [@sitiom](https://github.com/sitiom) for getting _komorebi_ added to the popular Scoop Extras bucket.
### Building from Source
If you prefer to compile _komorebi_ from source, you will need
a [working Rust development environment on Windows 10](https://rustup.rs/). The `x86_64-pc-windows-msvc` toolchain is
a [working Rust development environment on Windows 10/11](https://rustup.rs/). The `x86_64-pc-windows-msvc` toolchain is
required, so make sure you have also installed
the [Build Tools for Visual Studio 2019](https://stackoverflow.com/a/55603112).
@@ -150,12 +239,11 @@ cargo install --path komorebic --locked
### Running
Once you have either the prebuilt binaries in your `Path`, or have compiled the binaries from source (these will already
be in your `Path` if you installed Rust with [rustup](https://rustup.rs), which you absolutely should), you can
run `komorebic start` at a Powershell prompt, and you will see the following output:
Run `komorebic start --await-configuration` at a Powershell prompt, and you will see the following output:
```
Start-Process komorebi -WindowStyle hidden
Start-Process komorebi.exe -ArgumentList '--await-configuration' -WindowStyle hidden
Waiting for komorebi.exe to start...Started!
```
This means that `komorebi` is now running in the background, tiling all your windows, and listening for commands sent to
@@ -163,78 +251,66 @@ it by `komorebic`. You can similarly stop the process by running `komorebic stop
### Configuring
Once `komorebi` is running, you can execute the `komorebi.sample.ahk` script to set up the default keybindings via AHK
(the file includes comments to help you start building your own configuration).
If you followed the quickstart, `komorebi` will find the sample `komorebi.ps1` file in your `$Env:USERPROFILE` directory
and automatically load it. This file also starts `whkd` using the sample `whkrc` file in your `$Env:USERPROFILE\.config`
directory.
If you have AutoHotKey installed and a `komorebi.ahk` file in your home directory (run `$Env:UserProfile` at a
PowerShell prompt to find your home directory), `komorebi` will automatically try to load it when starting.
Alternatively, if you have AutoHotKey installed and a `komorebi.ahk` file in `$Env:UserProfile` directory, `komorebi`
will automatically try to load it when starting.
There is also tentative support for loading a AutoHotKey v2 files, if the file is named `komorebi.ahk2` and
the `AutoHotKey64.exe` executable for AutoHotKey v2 is in your `Path`. If both `komorebi.ahk` and `komorebi.ahk2` files
exist in your home directory, only `komorebi.ahk` will be loaded. An example of an AutoHotKey v2 configuration file
for _komorebi_ can be found [here](https://gist.github.com/crosstyan/dafacc0778dabf693ce9236c57b201cd).
#### Configuration with `komorebic`
As previously mentioned, this project does not handle anything related to keybindings and shortcuts directly. I
personally use [`whkd`](https://github.com/LGUG2Z/whkd) to manage my window management shortcuts, and have provided a
sample [whkdrc](whkdrc.sample) configuration that you can use as a starting point for your own.
You can run `komorebic.exe` to get a full list of the commands that you can use to customise `komorebi` and create
keybindings with. You can run `komorebic.exe <COMMAND> --help` to get a full explanation of the arguments required for
each command.
You can run any configuration command in the `komorebi.ps1` file, and you can bind any action command to your desired
key combinations in the `whkdrc` file.
#### AutoHotKey Helper Library for `komorebic`
❗️**NOTE**: This section is only relevant for people who wish to use AutoHotKey instead of [`whkd`](https://github.com/LGUG2Z/whkd).
❗️**NOTE**: This helper library is only compatible with AutoHotKey v1.1, not with AutoHotKey v2.
Additionally, you may run `komorebic.exe ahk-library` to generate a helper library for AutoHotKey which wraps
every `komorebic` command in a native AHK function.
The output of this command is in AHKv1 syntax. It must be manually converted to AHKv2 syntax
using [this tool](https://github.com/mmikeww/AHK-v2-script-converter) or something similar.
If you include the generated library at the top of your `~/komorebi.ahk` configuration file, you will be able to call
any of the functions that it contains.
#### Using Different AHK Executables
❗️**NOTE**: This section is only relevant for people who wish to use AutoHotKey instead of [`whkd`](https://github.com/LGUG2Z/whkd).
The preferred way to install AutoHotKey for use with `komorebi` is to install it via `scoop`:
```powershell
scoop bucket add versions
scoop install autohotkey
```
If you install AutoHotKey using a different method, the name of the executable file may differ from the name given by
`scoop`, and thus what is expected by default in `komorebi`.
You may override the executables that `komorebi` looks for to launch and reload `komorebi.ahk` configuration files using
by setting one of the following two environment variables depending on which version of AutoHotKey you wish to use:
- `$Env:KOMOREBI_AHK_V1_EXE`
- `$Env:KOMOREBI_AHK_V2_EXE`
You may override the executable that `komorebi` looks for to launch and reload `komorebi.ahk` configuration files by
setting the `$Env:KOMOREBI_AHK_EXE` environment variable.
Please keep in mind that even when setting a custom executable name using these environment variables, the executables
are still required to be in your `Path`.
### Common First-Time Tips
#### Generating Common Application-Specific Configurations
A curated selection of application-specific configurations can be generated to
help ease the setup for first-time users.
[`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
contains YAML definitions of settings that are known to make tricky
applications behave as expected. These YAML definitions can be used to generate
an AHK file which you can import at the start of your own `komorebi.ahk` file,
leaving you to focus primarily on your desired keybindings and workspace
configurations.
If you have settings for an application that you think should be part of this
curated selection, please open a PR on the configuration repository.
In the event that your PR is not accepted, or if you find there are any
settings that you wish to override, this can easily be done using an override
file.
```powershell
# Clone and enter the repository
git clone https://github.com/LGUG2Z/komorebi-application-specific-configuration.git
cd komorebi-application-specific-configuration
# Use komorebic to generate an AHK file
komorebic.exe ahk-app-specific-configuration applications.yaml
# Application-specific generated configuration written to C:\Users\LGUG2Z\.config\komorebi\komorebi.generated.ahk
#
# You can include the generated configuration at the top of your komorebi.ahk config with this line:
#
# #Include %A_ScriptDir%\komorebi.generated.ahk
# Optionally, provide an override file that follows the same schema as the second argument
komorebic.exe ahk-app-specific-configuration applications.yaml overrides.yaml
```
#### Setting a Custom KOMOREBI_CONFIG_HOME Directory
If you do not want to keep _komorebi_-related files in your `$Env:UserProfile` directory, you can specify a custom directory
If you do not want to keep _komorebi_-related files in your `$Env:USERPROFILE` directory, you can specify a custom directory
by setting the `$Env:KOMOREBI_CONFIG_HOME` environment variable.
For example, to use the `~/.config/komorebi` directory:
@@ -258,28 +334,81 @@ 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.
#### Generating Common Application-Specific Configurations
A curated selection of application-specific configurations can be generated to
help ease the setup for first-time users.
[`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
contains YAML definitions of settings that are known to make tricky
applications behave as expected. These YAML definitions can be used to generate
a `ps1` or an `ahk` file which you can import at the start of your own `komorebi.ps1` or `komorebi.ahk` files,
leaving you to focus primarily on your desired keybindings and workspace
configurations.
If you have settings for an application that you think should be part of this
curated selection, please open a PR on the configuration repository.
In the event that your PR is not accepted, or if you find there are any
settings that you wish to override, this can easily be done using an override
file.
```powershell
# Clone and enter the repository
git clone https://github.com/LGUG2Z/komorebi-application-specific-configuration.git
cd komorebi-application-specific-configuration
# Use komorebic to generate a ps1 file
komorebic.exe pwsh-app-specific-configuration applications.yaml
# Application-specific generated configuration written to C:\Users\LGUG2Z\.config\komorebi\komorebi.generated.ps1
# Or use komorebic to generate an ahk file
komorebic.exe ahk-app-specific-configuration applications.yaml
# Application-specific generated configuration written to C:\Users\LGUG2Z\.config\komorebi\komorebi.generated.ahk
#
# You can include the generated configuration at the top of your komorebi.ahk config with this line:
#
# #Include %A_ScriptDir%\komorebi.generated.ahk
# Optionally, provide an override file that follows the same schema as the second argument
komorebic.exe pwsh-app-specific-configuration applications.yaml overrides.yaml
```
#### 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]
komorebic.exe active-window-border-colour [R G B] --window-kind single
# optionally, if you want a different colour for stacks of windows
komorebic.exe active-window-border-colour [R G B] --window-kind stack
# optionally, if you want a different colour for windows in monocle mode
komorebic.exe active-window-border-colour [R G B] --window-kind monocle
```
It is important to note that the active window border will only apply to windows managed by `komorebi`.
[![Watch the tutorial video](https://img.youtube.com/vi/ywiAvoMV_gE/hqdefault.jpg)](https://www.youtube.com/watch?v=ywiAvoMV_gE)
#### 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:
```powershell
komorebic.exe container-padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
komorebic.exe workspace padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
komorebic.exe workspace-padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
```
[![Watch the tutorial video](https://img.youtube.com/vi/eGr07mymgWE/hqdefault.jpg)](https://www.youtube.com/watch?v=eGr07mymgWE)
#### Multiple Layout Changes on Startup
❗️**NOTE**: If you followed the quickstart and are using the sample configurations, this is already the default behaviour.
Depending on what is in your configuration, when `komorebi` is started, you may experience the layout rapidly being adjusted
with many retile events.
@@ -287,13 +416,16 @@ If you would like to avoid this, you can start `komorebi` with a flag which tell
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.
command at the end of the configuration section of your `komorebi.ps1` (or `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
❗️**NOTE**: A significant number of floating window rules for the most common applications are
[already generated for you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
Sometimes you will want a specific application to never be tiled, and instead float all the time. You can add rules to
enforce this behaviour:
```powershell
@@ -304,6 +436,9 @@ komorebic.exe float-rule title "Control Panel"
#### Windows Not Getting Managed
❗️**NOTE**: A significant number of force-manage window rules for the most common applications are
[already generated for you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
In some rare cases, a window may not automatically be registered to be managed by `komorebi`. When this happens, you can
manually add a rule to force `komorebi` to manage it:
@@ -315,6 +450,9 @@ komorebic.exe manage-rule exe TIM.exe
#### Tray Applications
❗️**NOTE**: A significant number of tray application rules for the most common applications are
[already generated for you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
If you are experiencing behaviour where
[closing a window leaves a blank tile, but minimizing the same window does not](https://github.com/LGUG2Z/komorebi/issues/6)
, you have probably enabled a 'close/minimize to tray' option for that application. You can tell _komorebi_ to handle
@@ -328,6 +466,9 @@ komorebic.exe identify-tray-application exe Discord.exe
#### Microsoft Office Applications
❗️**NOTE**: Microsoft Office-specific application rules are
[already generated for you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
Microsoft Office applications such as Word and Excel require certain configuration options to be set in order to be
managed correctly. Below is an example of configuring Microsoft Word to be managed correctly by _komorebi_.
@@ -370,10 +511,12 @@ By default, the mouse will move to the center of the window when the focus is ch
behaviour is know is 'mouse follows focus'. To disable this behaviour across all workspaces, add the following command
to your configuration file:
```ahk
Run, komorebic.exe toggle-mouse-follows-focus, , Hide
```powershell
komorebic.exe mouse-follows-focus disable
```
[![Watch the tutorial video](https://img.youtube.com/vi/LBoyXQiNINc/hqdefault.jpg)](https://www.youtube.com/watch?v=LBoyXQiNINc)
#### Saving and Loading Resized Layouts
If you create a BSP layout through various resize adjustments that you want to be able to restore easily in the future,
@@ -445,6 +588,8 @@ YAML
configuration: Horizontal
```
[![Watch the tutorial video](https://img.youtube.com/vi/SgmBHKEOcQ4/hqdefault.jpg)](https://www.youtube.com/watch?v=SgmBHKEOcQ4)
#### Dynamically Changing Layouts Based on Number of Visible Window Containers
With `komorebi` it is possible to define rules to automatically change the layout on a specified workspace when a
@@ -470,172 +615,6 @@ layout rules for that workspace have been cleared.
komorebic clear-workspace-layout-rules 0 0
```
## Configuration with `komorebic`
As previously mentioned, this project does not handle anything related to keybindings and shortcuts directly. I
personally use AutoHotKey to manage my window management shortcuts, and have provided a
sample [komorebi.ahk](komorebi.sample.ahk) AHK script that you can use as a starting point for your own.
You can run `komorebic.exe` to get a full list of the commands that you can use to customise `komorebi` and create
keybindings with. You can run `komorebic.exe <COMMAND> --help` to get a full explanation of the arguments required for
each command.
```
start Start komorebi.exe as a background process
stop Stop the komorebi.exe process and restore all hidden windows
state Show a JSON representation of the current window manager state
query Query the current window manager state
subscribe Subscribe to komorebi events
unsubscribe Unsubscribe from komorebi events
log Tail komorebi.exe's process logs (cancel with Ctrl-C)
quick-save-resize Quicksave the current resize layout dimensions
quick-load-resize Load the last quicksaved resize layout dimensions
save-resize Save the current resize layout dimensions to a file
load-resize Load the resize layout dimensions from a file
focus Change focus to the window in the specified direction
move Move the focused window in the specified direction
cycle-focus Change focus to the window in the specified cycle direction
cycle-move Move the focused window in the specified cycle direction
stack Stack the focused window in the specified direction
resize-edge Resize the focused window in the specified direction
resize-axis Resize the focused window or primary column along the specified axis
unstack Unstack the focused window
cycle-stack Cycle the focused stack in the specified cycle direction
move-to-monitor Move the focused window to the specified monitor
move-to-workspace Move the focused window to the specified workspace
send-to-monitor Send the focused window to the specified monitor
send-to-workspace Send the focused window to the specified workspace
send-to-monitor-workspace Send the focused window to the specified monitor workspace
focus-monitor Focus the specified monitor
focus-workspace Focus the specified workspace on the focused monitor
focus-monitor-workspace Focus the specified workspace on the target monitor
cycle-monitor Focus the monitor in the given cycle direction
cycle-workspace Focus the workspace in the given cycle direction
move-workspace-to-monitor Move the focused workspace to the specified monitor
new-workspace Create and append a new workspace on the focused monitor
resize-delta Set the resize delta (used by resize-edge and resize-axis)
invisible-borders Set the invisible border dimensions around each window
work-area-offset Set offsets to exclude parts of the work area from tiling
adjust-container-padding Adjust container padding on the focused workspace
adjust-workspace-padding Adjust workspace padding on the focused workspace
change-layout Set the layout on the focused workspace
load-custom-layout Load a custom layout from file for the focused workspace
flip-layout Flip the layout on the focused workspace (BSP only)
promote Promote the focused window to the top of the tree
retile Force the retiling of all managed windows
ensure-workspaces Create at least this many workspaces for the specified monitor
container-padding Set the container padding for the specified workspace
workspace-padding Set the workspace padding for the specified workspace
workspace-layout Set the layout for the specified workspace
workspace-custom-layout Set a custom layout for the specified workspace
workspace-layout-rule Add a dynamic layout rule for the specified workspace
workspace-custom-layout-rule Add a dynamic custom layout for the specified workspace
clear-workspace-layout-rules Clear all dynamic layout rules for the specified workspace
workspace-tiling Enable or disable window tiling for the specified workspace
workspace-name Set the workspace name for the specified workspace
toggle-window-container-behaviour Toggle the behaviour for new windows (stacking or dynamic tiling)
toggle-pause Toggle window tiling on the focused workspace
toggle-tiling Toggle window tiling on the focused workspace
toggle-float Toggle floating mode for the focused window
toggle-monocle Toggle monocle mode for the focused container
toggle-maximize Toggle native maximization for the focused window
restore-windows Restore all hidden windows (debugging command)
manage Force komorebi to manage the focused window
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
unmanaged-window-operation-behaviour Set the operation behaviour when the focused window is not managed
float-rule Add a rule to always float the specified application
manage-rule Add a rule to always manage the specified application
workspace-rule Add a rule to associate an application with a workspace
identify-object-name-change-application Identify an application that sends EVENT_OBJECT_NAMECHANGE on launch
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
toggle-mouse-follows-focus Toggle mouse follows focus on all workspaces
ahk-library Generate a library of AutoHotKey helper functions
ahk-app-specific-configuration Generate common app-specific configurations and fixes to use in komorebi.ahk
format-app-specific-configuration Format a YAML file for use with the 'ahk-app-specific-configuration' command
notification-schema Generate a JSON Schema of subscription notifications
help Print this message or the help of the given subcommand(s)
```
### AutoHotKey Helper Library for `komorebic`
Additionally, you may run `komorebic.exe ahk-library` to
generate [a helper library for AutoHotKey](komorebic.lib.sample.ahk) which wraps every `komorebic` command in a native
AHK function.
If you include the generated library at the top of your `~/komorebi.ahk` configuration file, you will be able to call
any of the functions that it contains. A sample AHK script that shows how this library can be
used [is available here](komorebi.sample.with.lib.ahk).
## Features
- [x] Multi-monitor
- [x] Virtual workspaces
- [x] Window stacks
- [x] Cycle through stacked windows
- [x] Change focused window by direction
- [x] Change focused window by direction across monitor boundary
- [x] Move focused window container in direction
- [x] Move focused window container in direction across monitor boundary
- [x] Move focused window container to monitor and follow
- [x] Move focused window container to workspace follow
- [x] Send focused window container to monitor
- [x] Send focused window container to workspace
- [x] Move focused workspace to monitor
- [x] Mouse follows focused container
- [x] Resize window container in direction
- [x] Resize window container on axis
- [x] Set custom resize delta
- [ ] Resize child window containers by split ratio
- [x] Quicksave and quickload layouts with resize dimensions
- [x] Save and load layouts with resize dimensions to/from specific files
- [x] Mouse drag to swap window container position
- [x] Mouse drag to resize window container
- [x] Configurable workspace and container gaps
- [x] BSP tree layout (`bsp`)
- [x] Flip BSP tree layout horizontally or vertically
- [x] Equal-width, max-height column layout (`columns`)
- [x] Equal-height, max-width row layout (`rows`)
- [x] Main half-height window with vertical stack layout (`horizontal-stack`)
- [x] Main half-width window with horizontal stack layout (`vertical-stack`)
- [x] 2x Main window (half and quarter-width) with horizontal stack layout (`ultrawide-vertical-stack`)
- [x] Load custom layouts from JSON and YAML representations
- [x] Dynamically select layout based on the number of open windows
- [x] Floating rules based on exe name, window title and class
- [x] Workspace rules based on exe name and window class
- [x] Additional manage rules based on exe name and window class
- [x] Identify applications which overflow their borders by exe name and class
- [x] Identify 'close/minimize to tray' applications by exe name and class
- [x] Configure work area offsets to preserve space for custom taskbars
- [x] Configure and compensate for the size of Windows 10's invisible borders
- [x] Toggle floating windows
- [x] Toggle monocle window
- [x] Toggle native maximization
- [x] Toggle mouse follows focus
- [x] Toggle Xmouse/Windows focus follows mouse implementation
- [x] Toggle Komorebi focus follows mouse implementation (desktop and system tray-aware)
- [x] Toggle automatic tiling
- [x] Pause all window management
- [x] Load configuration on startup
- [x] Manually reload configuration
- [x] Watch configuration for changes
- [x] Helper library for AutoHotKey
- [x] View window manager state
- [x] Query window manager state
- [x] Subscribe to event and message notifications
## Development
If you would like to contribute code to this repository, there are a few requests that I have to ensure a foundation of
@@ -729,9 +708,47 @@ An example of how to create a named pipe and a subscription to `komorebi`'s hand
by [@denBot](https://github.com/denBot) can be
found [here](https://gist.github.com/denBot/4136279812f87819f86d99eba77c1ee0).
An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Rust can also be found
in the [`komokana`](https://github.com/LGUG2Z/komokana) repository.
### Subscription Event Notification Schema
A [JSON Schema](https://json-schema.org/) of the event notifications emitted to subscribers can be generated with
the `komorebic notification-schema` command. The output of this command can be redirected to the clipboard or a file,
which can be used with services such as [Quicktype](https://app.quicktype.io/) to generate type definitions in different
programming languages.
### Communication over TCP
A TCP listener can optionally be exposed on a port of your choosing with the `--tcp-port=N` flag. If this flag is not
provided to `komorebi` or `komorebic start`, no TCP listener will be created.
Once created, your client may send
any [SocketMessage](https://github.com/LGUG2Z/komorebi/blob/master/komorebi-core/src/lib.rs#L37) to `komorebi` in the
same way that `komorebic` would.
This can be used if you would like to create your own alternative to `komorebic` which incorporates scripting and
various middleware layers, and similarly it can be used if you would like to integrate `komorebi` with
a [custom input handler](https://github.com/LGUG2Z/komorebi/issues/176#issue-1302643961).
If a client sends an unrecognized message, it will be disconnected and have to reconnect before trying to communicate
again.
### Socket Message Schema
A [JSON Schema](https://json-schema.org/) of socket messages used to send instructions to `komorebi` can be generated
with the `komorebic socket-schema` command. The output of this command can be redirected to the clipboard or a file,
which can be used with services such as [Quicktype](https://app.quicktype.io/) to generate type definitions in different
programming languages.
## Appreciations
- First and foremost, thank you to my wife, both for naming this project and for her patience throughout its never-ending development
- Thank you to [@sitiom](https://github.com/sitiom) for being [an exemplary open source community leader](https://jeezy.substack.com/p/the-open-source-contributions-i-appreciate)
- Thank you to the developers of [nog](https://github.com/TimUntersberger/nog) who came before me and whose work taught me more than I can ever hope to repay
- Thank you to the developers of [GlazeWM](https://github.com/lars-berger/GlazeWM) for pushing the boundaries of tiling window management on Windows with me and having an excellent spirit of collaboration
- Thank you to [@Ciantic](https://github.com/Ciantic) for helping me bring the [hidden Virtual Desktops cloaking function](https://github.com/Ciantic/AltTabAccessor/issues/1) to `komorebi`

View File

@@ -1,6 +1,6 @@
[package]
name = "derive-ahk"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
[lib]

View File

@@ -5,12 +5,15 @@
use ::std::clone::Clone;
use ::std::convert::From;
use ::std::convert::Into;
use ::std::format;
use ::std::iter::Extend;
use ::std::iter::Iterator;
use ::std::matches;
use ::std::option::Option::Some;
use ::std::string::String;
use ::std::string::ToString;
use ::std::unreachable;
use ::std::vec::Vec;
use ::quote::quote;
use ::syn::parse_macro_input;
@@ -102,7 +105,8 @@ pub fn ahk_function(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStre
let flag_idents_clone = flag_idents.clone();
let flags = quote! {#(--#flag_idents_clone) *}
.to_string()
.replace("- - ", "--");
.replace("- - ", "--")
.replace('_', "-");
let called_flag_arguments = quote! {#(%#flag_idents%) *}
.to_string()
@@ -110,19 +114,28 @@ pub fn ahk_function(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStre
.replace("% ", "%")
.replace("%%", "% %");
let flags_split: Vec<_> = flags.split(' ').collect();
let flag_args_split: Vec<_> = called_flag_arguments.split(' ').collect();
let mut consolidated_flags: Vec<String> = Vec::new();
for (idx, flag) in flags_split.iter().enumerate() {
consolidated_flags.push(format!("{} {}", flag, flag_args_split[idx]));
}
let all_flags = consolidated_flags.join(" ");
quote! {
impl AhkFunction for #name {
fn generate_ahk_function() -> String {
::std::format!(r#"
{}({}) {{
Run, komorebic.exe {} {} {} {}, , Hide
RunWait, komorebic.exe {} {} {}, , Hide
}}"#,
::std::stringify!(#name),
#all_arguments,
::std::stringify!(#name).to_kebab_case(),
#called_arguments,
#flags,
#called_flag_arguments
#all_flags,
)
}
}
@@ -135,7 +148,7 @@ pub fn ahk_function(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStre
fn generate_ahk_function() -> String {
::std::format!(r#"
{}({}) {{
Run, komorebic.exe {} {}, , Hide
RunWait, komorebic.exe {} {}, , Hide
}}"#,
::std::stringify!(#name),
#arguments,
@@ -181,7 +194,7 @@ pub fn ahk_library(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStrea
stream.extend(quote! {
v.push(::std::format!(r#"
{}() {{
Run, komorebic.exe {}, , Hide
RunWait, komorebic.exe {}, , Hide
}}"#,
::std::stringify!(#name),
::std::stringify!(#name).to_kebab_case()

View File

@@ -1,4 +1,4 @@
set shell := ["cmd.exe", "/C"]
set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"]
export RUST_BACKTRACE := "full"
clean:
@@ -8,6 +8,10 @@ fmt:
cargo +nightly fmt
cargo +stable clippy
prettier --write README.md
prettier --write .goreleaser.yml
prettier --write .github/dependabot.yml
prettier --write .github/FUNDING.yml
prettier --write .github/workflows/windows.yaml
install-komorebic:
cargo +stable install --path komorebic --locked
@@ -18,12 +22,15 @@ install-komorebi:
install:
just install-komorebic
just install-komorebi
komorebic ahk-library
cat '%USERPROFILE%\.config\komorebi\komorebic.lib.ahk' > komorebic.lib.sample.ahk
komorebic ahk-asc '~/komorebi-application-specific-configuration/applications.yaml'
komorebic pwsh-asc '~/komorebi-application-specific-configuration/applications.yaml'
cat '~/.config/komorebi/komorebi.generated.ps1' >komorebi.generated.ps1
cat '~/.config/komorebi/komorebi.generated.ahk' >komorebi.generated.ahk
cat '~/.config/komorebi/komorebic.lib_newV2.ahk' >komorebic.lib.ahk
run:
just install-komorebic
cargo +stable run --bin komorebi --locked
cargo +stable run --bin komorebi --locked -- -a
warn $RUST_LOG="warn":
just run

View File

@@ -1,12 +1,12 @@
[package]
name = "komorebi-core"
version = "0.1.12"
version = "0.1.16"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "3", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -15,7 +15,7 @@ strum = { version = "0.24", features = ["derive"] }
schemars = "0.8"
[dependencies.windows]
version = "0.39"
version = "0.48"
features = [
"Win32_Foundation",
]

View File

@@ -1,6 +1,6 @@
use std::num::NonZeroUsize;
use clap::ArgEnum;
use clap::ValueEnum;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
@@ -342,7 +342,9 @@ impl Arrangement for CustomLayout {
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum Axis {
Horizontal,

View File

@@ -1,4 +1,4 @@
use clap::ArgEnum;
use clap::ValueEnum;
use color_eyre::Result;
use schemars::JsonSchema;
use serde::Deserialize;
@@ -8,7 +8,7 @@ use strum::EnumString;
use crate::ApplicationIdentifier;
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ApplicationOptions {
@@ -20,65 +20,70 @@ pub enum ApplicationOptions {
}
impl ApplicationOptions {
#[must_use]
pub fn raw_cfgen(&self, kind: &ApplicationIdentifier, id: &str) -> String {
match self {
ApplicationOptions::ObjectNameChange => {
format!(
"komorebic.exe identify-object-name-change-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::Layered => {
format!(
"komorebic.exe identify-layered-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::BorderOverflow => {
format!(
"komorebic.exe identify-border-overflow-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::TrayAndMultiWindow => {
format!(
"komorebic.exe identify-tray-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::Force => {
format!("komorebic.exe manage-rule {} \"{}\"", kind, id)
}
}
}
#[must_use]
pub fn cfgen(&self, kind: &ApplicationIdentifier, id: &str) -> String {
format!(
"Run, {}, , Hide",
match self {
ApplicationOptions::ObjectNameChange => {
format!(
"komorebic.exe identify-object-name-change-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::Layered => {
format!(
"komorebic.exe identify-layered-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::BorderOverflow => {
format!(
"komorebic.exe identify-border-overflow-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::TrayAndMultiWindow => {
format!(
"komorebic.exe identify-tray-application {} \"{}\"",
kind, id
)
}
ApplicationOptions::Force => {
format!("komorebic.exe manage-rule {} \"{}\"", kind, id)
}
}
"RunWait('{}', , \"Hide\")",
ApplicationOptions::raw_cfgen(self, kind, id)
)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifier {
kind: ApplicationIdentifier,
id: String,
pub kind: ApplicationIdentifier,
pub id: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifierAndComment {
kind: ApplicationIdentifier,
id: String,
pub kind: ApplicationIdentifier,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
comment: Option<String>,
pub comment: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
pub struct ApplicationConfiguration {
name: String,
identifier: IdWithIdentifier,
pub name: String,
pub identifier: IdWithIdentifier,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<Vec<ApplicationOptions>>,
pub options: Option<Vec<ApplicationOptions>>,
#[serde(skip_serializing_if = "Option::is_none")]
float_identifiers: Option<Vec<IdWithIdentifierAndComment>>,
pub float_identifiers: Option<Vec<IdWithIdentifierAndComment>>,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
@@ -118,6 +123,58 @@ impl ApplicationConfigurationGenerator {
Ok(final_cfgen)
}
pub fn generate_pwsh(
base_content: &str,
override_content: Option<&str>,
) -> Result<Vec<String>> {
let mut cfgen = if let Some(override_content) = override_content {
Self::merge(base_content, override_content)?
} else {
Self::load(base_content)?
};
cfgen.sort_by(|a, b| a.name.cmp(&b.name));
let mut lines = vec![String::from("# Generated by komorebic.exe"), String::new()];
let mut float_rules = vec![];
for app in cfgen {
lines.push(format!("# {}", app.name));
if let Some(options) = app.options {
for opt in options {
if let ApplicationOptions::TrayAndMultiWindow = opt {
lines.push(String::from("# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line"));
}
lines.push(opt.raw_cfgen(&app.identifier.kind, &app.identifier.id));
}
}
if let Some(float_identifiers) = app.float_identifiers {
for float in float_identifiers {
let float_rule =
format!("komorebic.exe float-rule {} \"{}\"", float.kind, float.id);
// Don't want to send duped signals especially as configs get larger
if !float_rules.contains(&float_rule) {
float_rules.push(float_rule.clone());
if let Some(comment) = float.comment {
lines.push(format!("# {}", comment));
};
lines.push(float_rule);
}
}
}
lines.push(String::new());
}
Ok(lines)
}
pub fn generate_ahk(base_content: &str, override_content: Option<&str>) -> Result<Vec<String>> {
let mut cfgen = if let Some(override_content) = override_content {
Self::merge(base_content, override_content)?
@@ -127,12 +184,7 @@ impl ApplicationConfigurationGenerator {
cfgen.sort_by(|a, b| a.name.cmp(&b.name));
let mut lines = vec![
String::from("; Generated by komorebic.exe"),
String::from("; To use this file, add the line below to the top of your komorebi.ahk configuration file"),
String::from("; #Include %A_ScriptDir%\\komorebi.generated.ahk"),
String::from("")
];
let mut lines = vec![String::from("; Generated by komorebic.exe"), String::new()];
let mut float_rules = vec![];
@@ -151,7 +203,7 @@ impl ApplicationConfigurationGenerator {
if let Some(float_identifiers) = app.float_identifiers {
for float in float_identifiers {
let float_rule = format!(
"Run, komorebic.exe float-rule {} \"{}\", , Hide",
"RunWait('komorebic.exe float-rule {} \"{}\"', , \"Hide\")",
float.kind, float.id
);
@@ -168,7 +220,7 @@ impl ApplicationConfigurationGenerator {
}
}
lines.push(String::from(""));
lines.push(String::new());
}
Ok(lines)

View File

@@ -1,13 +1,15 @@
use std::num::NonZeroUsize;
use clap::ArgEnum;
use clap::ValueEnum;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use strum::Display;
use strum::EnumString;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum CycleDirection {
Previous,

View File

@@ -1,4 +1,4 @@
use clap::ArgEnum;
use clap::ValueEnum;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
@@ -9,7 +9,9 @@ use crate::OperationDirection;
use crate::Rect;
use crate::Sizing;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum DefaultLayout {
BSP,

View File

@@ -229,16 +229,13 @@ impl Direction for CustomLayout {
}
let (column_idx, column) = self.column_with_idx(idx);
match column {
None => false,
Some(column) => match column {
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
| Column::Tertiary(ColumnSplit::Horizontal) => {
self.column_for_container_idx(idx - 1) == column_idx
}
_ => false,
},
}
column.map_or(false, |column| match column {
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
| Column::Tertiary(ColumnSplit::Horizontal) => {
self.column_for_container_idx(idx - 1) == column_idx
}
_ => false,
})
}
OperationDirection::Down => {
if idx == count - 1 {
@@ -246,16 +243,13 @@ impl Direction for CustomLayout {
}
let (column_idx, column) = self.column_with_idx(idx);
match column {
None => false,
Some(column) => match column {
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
| Column::Tertiary(ColumnSplit::Horizontal) => {
self.column_for_container_idx(idx + 1) == column_idx
}
_ => false,
},
}
column.map_or(false, |column| match column {
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
| Column::Tertiary(ColumnSplit::Horizontal) => {
self.column_for_container_idx(idx + 1) == column_idx
}
_ => false,
})
}
}
}

View File

@@ -4,7 +4,7 @@
use std::path::PathBuf;
use std::str::FromStr;
use clap::ArgEnum;
use clap::ValueEnum;
use color_eyre::Result;
use schemars::JsonSchema;
use serde::Deserialize;
@@ -46,12 +46,22 @@ pub enum SocketMessage {
UnstackWindow,
CycleStack(CycleDirection),
MoveContainerToMonitorNumber(usize),
CycleMoveContainerToMonitor(CycleDirection),
MoveContainerToWorkspaceNumber(usize),
MoveContainerToNamedWorkspace(String),
CycleMoveContainerToWorkspace(CycleDirection),
SendContainerToMonitorNumber(usize),
CycleSendContainerToMonitor(CycleDirection),
SendContainerToWorkspaceNumber(usize),
CycleSendContainerToWorkspace(CycleDirection),
SendContainerToMonitorWorkspaceNumber(usize, usize),
SendContainerToNamedWorkspace(String),
MoveWorkspaceToMonitorNumber(usize),
ForceFocus,
Close,
Minimize,
Promote,
PromoteFocus,
ToggleFloat,
ToggleMonocle,
ToggleMaximize,
@@ -69,7 +79,9 @@ pub enum SocketMessage {
ChangeLayoutCustom(PathBuf),
FlipLayout(Axis),
// Monitor and Workspace Commands
MonitorIndexPreference(usize, i32, i32, i32, i32),
EnsureWorkspaces(usize, usize),
EnsureNamedWorkspaces(usize, Vec<String>),
NewWorkspace,
ToggleTiling,
Stop,
@@ -84,25 +96,41 @@ pub enum SocketMessage {
FocusMonitorNumber(usize),
FocusWorkspaceNumber(usize),
FocusMonitorWorkspaceNumber(usize, usize),
FocusNamedWorkspace(String),
ContainerPadding(usize, usize, i32),
NamedWorkspaceContainerPadding(String, i32),
WorkspacePadding(usize, usize, i32),
NamedWorkspacePadding(String, i32),
WorkspaceTiling(usize, usize, bool),
NamedWorkspaceTiling(String, bool),
WorkspaceName(usize, usize, String),
WorkspaceLayout(usize, usize, DefaultLayout),
NamedWorkspaceLayout(String, DefaultLayout),
WorkspaceLayoutCustom(usize, usize, PathBuf),
NamedWorkspaceLayoutCustom(String, PathBuf),
WorkspaceLayoutRule(usize, usize, usize, DefaultLayout),
NamedWorkspaceLayoutRule(String, usize, DefaultLayout),
WorkspaceLayoutCustomRule(usize, usize, usize, PathBuf),
NamedWorkspaceLayoutCustomRule(String, usize, PathBuf),
ClearWorkspaceLayoutRules(usize, usize),
ClearNamedWorkspaceLayoutRules(String),
// Configuration
ReloadConfiguration,
WatchConfiguration(bool),
CompleteConfiguration,
AltFocusHack(bool),
ActiveWindowBorder(bool),
ActiveWindowBorderColour(u32, u32, u32),
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
ActiveWindowBorderWidth(i32),
ActiveWindowBorderOffset(i32),
InvisibleBorders(Rect),
WorkAreaOffset(Rect),
MonitorWorkAreaOffset(usize, Rect),
ResizeDelta(i32),
InitialWorkspaceRule(ApplicationIdentifier, String, usize, usize),
InitialNamedWorkspaceRule(ApplicationIdentifier, String, String),
WorkspaceRule(ApplicationIdentifier, String, usize, usize),
NamedWorkspaceRule(ApplicationIdentifier, String, String),
FloatRule(ApplicationIdentifier, String),
ManageRule(ApplicationIdentifier, String),
IdentifyObjectNameChangeApplication(ApplicationIdentifier, String),
@@ -118,6 +146,7 @@ pub enum SocketMessage {
AddSubscriber(String),
RemoveSubscriber(String),
NotificationSchema,
SocketSchema,
}
impl SocketMessage {
@@ -134,7 +163,19 @@ impl FromStr for SocketMessage {
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum WindowKind {
Single,
Stack,
Monocle,
}
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum StateQuery {
FocusedMonitorIndex,
@@ -143,7 +184,9 @@ pub enum StateQuery {
FocusedWindowIndex,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum ApplicationIdentifier {
@@ -152,42 +195,55 @@ pub enum ApplicationIdentifier {
Title,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum FocusFollowsMouseImplementation {
Komorebi,
Windows,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum WindowContainerBehaviour {
Create,
Append,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum MoveBehaviour {
Swap,
Insert,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum HidingBehaviour {
Hide,
Minimize,
Cloak,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum OperationBehaviour {
Op,
NoOp,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum Sizing {
Increase,

View File

@@ -1,6 +1,6 @@
use std::num::NonZeroUsize;
use clap::ArgEnum;
use clap::ValueEnum;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
@@ -10,7 +10,9 @@ use strum::EnumString;
use crate::direction::Direction;
use crate::Axis;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum OperationDirection {
Left,

499
komorebi.generated.ahk Normal file
View File

@@ -0,0 +1,499 @@
; Generated by komorebic.exe
; 1Password
RunWait('komorebic.exe float-rule exe "1Password.exe"', , "Hide")
; Ableton Live
; Targets VST2 windows
RunWait('komorebic.exe float-rule class "AbletonVstPlugClass"', , "Hide")
; Targets VST3 windows
RunWait('komorebic.exe float-rule class "Vst3PlugWindow"', , "Hide")
; Adobe Creative Cloud
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application class "CreativeCloudDesktopWindowClass"', , "Hide")
; Adobe Photoshop
RunWait('komorebic.exe identify-border-overflow-application class "Photoshop"', , "Hide")
; Akiflow
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Akiflow.exe"', , "Hide")
; ArmCord
RunWait('komorebic.exe identify-border-overflow-application exe "ArmCord.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ArmCord.exe"', , "Hide")
; AutoHotkey
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "AutoHotkeyU64.exe"', , "Hide")
RunWait('komorebic.exe float-rule title "Window Spy"', , "Hide")
; Beeper
RunWait('komorebic.exe identify-border-overflow-application exe "Beeper.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Beeper.exe"', , "Hide")
; Bitwarden
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Bitwarden.exe"', , "Hide")
; Bloxstrap
RunWait('komorebic.exe float-rule exe "Bloxstrap.exe"', , "Hide")
; Calculator
RunWait('komorebic.exe float-rule title "Calculator"', , "Hide")
; Credential Manager UI Host
; Targets the Windows popup prompting you for a PIN instead of a password on 1Password etc.
RunWait('komorebic.exe float-rule exe "CredentialUIBroker.exe"', , "Hide")
; Cron
RunWait('komorebic.exe identify-border-overflow-application exe "Cron.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Cron.exe"', , "Hide")
; Delphi applications
; Target hidden window spawned by Delphi applications
RunWait('komorebic.exe float-rule class "TApplication"', , "Hide")
; Target Inno Setup installers
RunWait('komorebic.exe float-rule class "TWizardForm"', , "Hide")
; Discord
RunWait('komorebic.exe identify-border-overflow-application exe "Discord.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Discord.exe"', , "Hide")
; DiscordCanary
RunWait('komorebic.exe identify-border-overflow-application exe "DiscordCanary.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "DiscordCanary.exe"', , "Hide")
; DiscordDevelopment
RunWait('komorebic.exe identify-border-overflow-application exe "DiscordDevelopment.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "DiscordDevelopment.exe"', , "Hide")
; DiscordPTB
RunWait('komorebic.exe identify-border-overflow-application exe "DiscordPTB.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "DiscordPTB.exe"', , "Hide")
; Dropbox
RunWait('komorebic.exe float-rule exe "Dropbox.exe"', , "Hide")
; ElectronMail
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ElectronMail.exe"', , "Hide")
; Element
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Element.exe"', , "Hide")
; Elephicon
RunWait('komorebic.exe float-rule exe "Elephicon.exe"', , "Hide")
; ElevenClock
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ElevenClock.exe"', , "Hide")
; Elgato Camera Hub
RunWait('komorebic.exe float-rule exe "Camera Hub.exe"', , "Hide")
; Elgato Control Center
RunWait('komorebic.exe float-rule exe "ControlCenter.exe"', , "Hide")
; Elgato Wave Link
RunWait('komorebic.exe float-rule exe "WaveLink.exe"', , "Hide")
; Epic Games Launcher
RunWait('komorebic.exe identify-border-overflow-application exe "EpicGamesLauncher.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "EpicGamesLauncher.exe"', , "Hide")
; Flow Launcher
RunWait('komorebic.exe identify-border-overflow-application exe "Flow.Launcher.exe"', , "Hide")
; GOG Galaxy
RunWait('komorebic.exe identify-border-overflow-application exe "GalaxyClient.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "GalaxyClient.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "GalaxyClient.exe"', , "Hide")
; Targets a hidden window spawned by GOG Galaxy
RunWait('komorebic.exe float-rule class "Chrome_RenderWidgetHostHWND"', , "Hide")
; GoPro Webcam
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application class "GoPro Webcam"', , "Hide")
; Godot Manager
RunWait('komorebic.exe identify-border-overflow-application exe "GodotManager.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "GodotManager.exe"', , "Hide")
RunWait('komorebic.exe identify-object-name-change-application exe "GodotManager.exe"', , "Hide")
; Google Chrome
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "chrome.exe"', , "Hide")
; Google Drive
RunWait('komorebic.exe float-rule exe "GoogleDriveFS.exe"', , "Hide")
; Houdoku
RunWait('komorebic.exe identify-border-overflow-application exe "Houdoku.exe"', , "Hide")
; IntelliJ IDEA
RunWait('komorebic.exe identify-object-name-change-application exe "idea64.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "idea64.exe"', , "Hide")
; Targets JetBrains IDE popups and floating windows
RunWait('komorebic.exe float-rule class "SunAwtDialog"', , "Hide")
; Itch.io
RunWait('komorebic.exe identify-border-overflow-application exe "itch.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "itch.exe"', , "Hide")
; Keyviz
RunWait('komorebic.exe float-rule exe "keyviz.exe"', , "Hide")
; Kleopatra
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "kleopatra.exe"', , "Hide")
; Kotatogram
RunWait('komorebic.exe identify-border-overflow-application exe "Kotatogram.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Kotatogram.exe"', , "Hide")
; LocalSend
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "localsend_app.exe"', , "Hide")
; Logi Bolt
RunWait('komorebic.exe float-rule exe "LogiBolt.exe"', , "Hide")
; LogiTune
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "LogiTune.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "LogiTune.exe"', , "Hide")
; Logitech G HUB
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "lghub.exe"', , "Hide")
RunWait('komorebic.exe identify-border-overflow-application exe "lghub.exe"', , "Hide")
; Logitech Options
RunWait('komorebic.exe float-rule exe "LogiOptionsUI.exe"', , "Hide")
; Mailspring
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "mailspring.exe"', , "Hide")
; ManyCam
RunWait('komorebic.exe identify-border-overflow-application exe "ManyCam.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ManyCam.exe"', , "Hide")
; Mica For Everyone
; Microsoft Excel
RunWait('komorebic.exe identify-border-overflow-application exe "EXCEL.EXE"', , "Hide")
RunWait('komorebic.exe identify-layered-application exe "EXCEL.EXE"', , "Hide")
; Targets a hidden window spawned by Microsoft Office applications
RunWait('komorebic.exe float-rule class "_WwB"', , "Hide")
; Microsoft Outlook
RunWait('komorebic.exe identify-border-overflow-application exe "OUTLOOK.EXE"', , "Hide")
RunWait('komorebic.exe identify-layered-application exe "OUTLOOK.EXE"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "OUTLOOK.EXE"', , "Hide")
; Microsoft PC Manager
RunWait('komorebic.exe float-rule exe "MSPCManager.exe"', , "Hide")
; Microsoft PowerPoint
RunWait('komorebic.exe identify-border-overflow-application exe "POWERPNT.EXE"', , "Hide")
RunWait('komorebic.exe identify-layered-application exe "POWERPNT.EXE"', , "Hide")
; Microsoft Teams
RunWait('komorebic.exe identify-border-overflow-application exe "Teams.exe"', , "Hide")
; Target Teams pop-up notification windows
RunWait('komorebic.exe float-rule title "Microsoft Teams Notification"', , "Hide")
; Target Teams call in progress windows
RunWait('komorebic.exe float-rule title "Microsoft Teams Call"', , "Hide")
; Microsoft Word
RunWait('komorebic.exe identify-border-overflow-application exe "WINWORD.EXE"', , "Hide")
RunWait('komorebic.exe identify-layered-application exe "WINWORD.EXE"', , "Hide")
; Modern Flyouts
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ModernFlyoutsHost.exe"', , "Hide")
; Mozilla Firefox
RunWait('komorebic.exe identify-object-name-change-application exe "firefox.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "firefox.exe"', , "Hide")
; Targets invisible windows spawned by Firefox to show tab previews in the taskbar
RunWait('komorebic.exe float-rule class "MozillaTaskbarPreviewClass"', , "Hide")
; NVIDIA GeForce Experience
RunWait('komorebic.exe identify-border-overflow-application exe "NVIDIA GeForce Experience.exe"', , "Hide")
; NZXT CAM
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "NZXT CAM.exe"', , "Hide")
; NiceHash Miner
RunWait('komorebic.exe identify-border-overflow-application exe "nhm_app.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "nhm_app.exe"', , "Hide")
; NohBoard
RunWait('komorebic.exe float-rule exe "NohBoard.exe"', , "Hide")
; Notion Enhanced
RunWait('komorebic.exe identify-border-overflow-application exe "Notion Enhanced.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Notion Enhanced.exe"', , "Hide")
; OBS Studio (32-bit)
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "obs32.exe"', , "Hide")
; OBS Studio (64-bit)
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "obs64.exe"', , "Hide")
; ONLYOFFICE Editors
RunWait('komorebic.exe identify-border-overflow-application class "DocEditorsWindowClass"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application class "DocEditorsWindowClass"', , "Hide")
; Obsidian
RunWait('komorebic.exe identify-border-overflow-application exe "Obsidian.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "Obsidian.exe"', , "Hide")
; OpenRGB
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "OpenRGB.exe"', , "Hide")
; Paradox Launcher
RunWait('komorebic.exe float-rule exe "Paradox Launcher.exe"', , "Hide")
; Plexamp
RunWait('komorebic.exe identify-border-overflow-application exe "Plexamp.exe"', , "Hide")
; PowerToys
; Target color picker dialog
RunWait('komorebic.exe float-rule exe "PowerToys.ColorPickerUI.exe"', , "Hide")
; Target image resizer dialog
RunWait('komorebic.exe float-rule exe "PowerToys.ImageResizer.exe"', , "Hide")
; Process Hacker
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ProcessHacker.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "ProcessHacker.exe"', , "Hide")
; ProtonVPN
RunWait('komorebic.exe identify-border-overflow-application exe "ProtonVPN.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ProtonVPN.exe"', , "Hide")
; PyCharm
RunWait('komorebic.exe identify-object-name-change-application exe "pycharm64.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "pycharm64.exe"', , "Hide")
; QtScrcpy
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "QtScrcpy.exe"', , "Hide")
; QuickLook
RunWait('komorebic.exe float-rule exe "QuickLook.exe"', , "Hide")
; RepoZ
RunWait('komorebic.exe float-rule exe "RepoZ.exe"', , "Hide")
; Rider
RunWait('komorebic.exe identify-object-name-change-application exe "rider64.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "rider64.exe"', , "Hide")
; Roblox FPS Unlocker
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "rbxfpsunlocker.exe"', , "Hide")
; RoundedTB
RunWait('komorebic.exe float-rule exe "RoundedTB.exe"', , "Hide")
; RoundedTB
RunWait('komorebic.exe identify-border-overflow-application exe "RoundedTB.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "RoundedTB.exe"', , "Hide")
; ShareX
RunWait('komorebic.exe identify-border-overflow-application exe "ShareX.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ShareX.exe"', , "Hide")
; Sideloadly
RunWait('komorebic.exe float-rule exe "sideloadly.exe"', , "Hide")
; Signal
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "signal.exe"', , "Hide")
; SiriKali
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "sirikali.exe"', , "Hide")
; Slack
RunWait('komorebic.exe identify-border-overflow-application exe "Slack.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "Slack.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Slack.exe"', , "Hide")
; Slack
RunWait('komorebic.exe identify-border-overflow-application exe "slack.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "slack.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "slack.exe"', , "Hide")
; Smart Install Maker
; Target hidden window spawned by installer
RunWait('komorebic.exe float-rule class "obj_App"', , "Hide")
; Target installer
RunWait('komorebic.exe float-rule class "obj_Form"', , "Hide")
; SoulseekQt
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "SoulseekQt.exe"', , "Hide")
; Spotify
RunWait('komorebic.exe identify-border-overflow-application exe "Spotify.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Spotify.exe"', , "Hide")
; Steam
RunWait('komorebic.exe identify-border-overflow-application class "vguiPopupWindow"', , "Hide")
; Steam Beta
RunWait('komorebic.exe identify-border-overflow-application class "SDL_app"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application class "SDL_app"', , "Hide")
; Stremio
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "stremio.exe"', , "Hide")
; System Informer
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "SystemInformer.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "SystemInformer.exe"', , "Hide")
; SystemSettings
RunWait('komorebic.exe float-rule class "Shell_Dialog"', , "Hide")
; Task Manager
RunWait('komorebic.exe float-rule class "TaskManagerWindow"', , "Hide")
; Telegram
RunWait('komorebic.exe identify-border-overflow-application exe "Telegram.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Telegram.exe"', , "Hide")
; TickTick
RunWait('komorebic.exe identify-border-overflow-application exe "TickTick.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "TickTick.exe"', , "Hide")
; TouchCursor
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "tcconfig.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "tcconfig.exe"', , "Hide")
; TranslucentTB
RunWait('komorebic.exe float-rule exe "TranslucentTB.exe"', , "Hide")
; TranslucentTB
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "TranslucentTB.exe"', , "Hide")
; Unreal Editor
RunWait('komorebic.exe identify-border-overflow-application exe "UnrealEditor.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "UnrealEditor.exe"', , "Hide")
; VRCX
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "VRCX.exe"', , "Hide")
; Visual Studio
RunWait('komorebic.exe identify-object-name-change-application exe "devenv.exe"', , "Hide")
; Visual Studio Code
RunWait('komorebic.exe identify-border-overflow-application exe "Code.exe"', , "Hide")
; Voice.ai
RunWait('komorebic.exe identify-border-overflow-application exe "VoiceAI.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "VoiceAI.exe"', , "Hide")
; WebTorrent Desktop
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "WebTorrent.exe"', , "Hide")
; WinZip (32-bit)
RunWait('komorebic.exe float-rule exe "winzip32.exe"', , "Hide")
; WinZip (64-bit)
RunWait('komorebic.exe float-rule exe "winzip64.exe"', , "Hide")
; Windows Console (conhost.exe)
RunWait('komorebic.exe manage-rule class "ConsoleWindowClass"', , "Hide")
; Windows Explorer
; Targets copy/move operation windows
RunWait('komorebic.exe float-rule class "OperationStatusWindow"', , "Hide")
RunWait('komorebic.exe float-rule title "Control Panel"', , "Hide")
; Windows Installer
RunWait('komorebic.exe float-rule exe "msiexec.exe"', , "Hide")
; WingetUI
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "WingetUI.exe"', , "Hide")
; WingetUI
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "wingetui.exe"', , "Hide")
; Wox
; Targets a hidden window spawned by Wox
RunWait('komorebic.exe float-rule title "Hotkey sink"', , "Hide")
; XAMPP Control Panel
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "xampp-control.exe"', , "Hide")
; Zoom
RunWait('komorebic.exe float-rule exe "Zoom.exe"', , "Hide")
; mpv.net
RunWait('komorebic.exe identify-object-name-change-application exe "mpvnet.exe"', , "Hide")
; paint.net
RunWait('komorebic.exe float-rule exe "paintdotnet.exe"', , "Hide")
; pinentry
RunWait('komorebic.exe float-rule exe "pinentry.exe"', , "Hide")
; qBittorrent
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "qbittorrent.exe"', , "Hide")
; ueli
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "ueli.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "ueli.exe"', , "Hide")

499
komorebi.generated.ps1 Normal file
View File

@@ -0,0 +1,499 @@
# Generated by komorebic.exe
# 1Password
komorebic.exe float-rule exe "1Password.exe"
# Ableton Live
# Targets VST2 windows
komorebic.exe float-rule class "AbletonVstPlugClass"
# Targets VST3 windows
komorebic.exe float-rule class "Vst3PlugWindow"
# Adobe Creative Cloud
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application class "CreativeCloudDesktopWindowClass"
# Adobe Photoshop
komorebic.exe identify-border-overflow-application class "Photoshop"
# Akiflow
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Akiflow.exe"
# ArmCord
komorebic.exe identify-border-overflow-application exe "ArmCord.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ArmCord.exe"
# AutoHotkey
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "AutoHotkeyU64.exe"
komorebic.exe float-rule title "Window Spy"
# Beeper
komorebic.exe identify-border-overflow-application exe "Beeper.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Beeper.exe"
# Bitwarden
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Bitwarden.exe"
# Bloxstrap
komorebic.exe float-rule exe "Bloxstrap.exe"
# Calculator
komorebic.exe float-rule title "Calculator"
# Credential Manager UI Host
# Targets the Windows popup prompting you for a PIN instead of a password on 1Password etc.
komorebic.exe float-rule exe "CredentialUIBroker.exe"
# Cron
komorebic.exe identify-border-overflow-application exe "Cron.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Cron.exe"
# Delphi applications
# Target hidden window spawned by Delphi applications
komorebic.exe float-rule class "TApplication"
# Target Inno Setup installers
komorebic.exe float-rule class "TWizardForm"
# Discord
komorebic.exe identify-border-overflow-application exe "Discord.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Discord.exe"
# DiscordCanary
komorebic.exe identify-border-overflow-application exe "DiscordCanary.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "DiscordCanary.exe"
# DiscordDevelopment
komorebic.exe identify-border-overflow-application exe "DiscordDevelopment.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "DiscordDevelopment.exe"
# DiscordPTB
komorebic.exe identify-border-overflow-application exe "DiscordPTB.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "DiscordPTB.exe"
# Dropbox
komorebic.exe float-rule exe "Dropbox.exe"
# ElectronMail
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ElectronMail.exe"
# Element
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Element.exe"
# Elephicon
komorebic.exe float-rule exe "Elephicon.exe"
# ElevenClock
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ElevenClock.exe"
# Elgato Camera Hub
komorebic.exe float-rule exe "Camera Hub.exe"
# Elgato Control Center
komorebic.exe float-rule exe "ControlCenter.exe"
# Elgato Wave Link
komorebic.exe float-rule exe "WaveLink.exe"
# Epic Games Launcher
komorebic.exe identify-border-overflow-application exe "EpicGamesLauncher.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "EpicGamesLauncher.exe"
# Flow Launcher
komorebic.exe identify-border-overflow-application exe "Flow.Launcher.exe"
# GOG Galaxy
komorebic.exe identify-border-overflow-application exe "GalaxyClient.exe"
komorebic.exe manage-rule exe "GalaxyClient.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "GalaxyClient.exe"
# Targets a hidden window spawned by GOG Galaxy
komorebic.exe float-rule class "Chrome_RenderWidgetHostHWND"
# GoPro Webcam
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application class "GoPro Webcam"
# Godot Manager
komorebic.exe identify-border-overflow-application exe "GodotManager.exe"
komorebic.exe manage-rule exe "GodotManager.exe"
komorebic.exe identify-object-name-change-application exe "GodotManager.exe"
# Google Chrome
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "chrome.exe"
# Google Drive
komorebic.exe float-rule exe "GoogleDriveFS.exe"
# Houdoku
komorebic.exe identify-border-overflow-application exe "Houdoku.exe"
# IntelliJ IDEA
komorebic.exe identify-object-name-change-application exe "idea64.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "idea64.exe"
# Targets JetBrains IDE popups and floating windows
komorebic.exe float-rule class "SunAwtDialog"
# Itch.io
komorebic.exe identify-border-overflow-application exe "itch.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "itch.exe"
# Keyviz
komorebic.exe float-rule exe "keyviz.exe"
# Kleopatra
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "kleopatra.exe"
# Kotatogram
komorebic.exe identify-border-overflow-application exe "Kotatogram.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Kotatogram.exe"
# LocalSend
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "localsend_app.exe"
# Logi Bolt
komorebic.exe float-rule exe "LogiBolt.exe"
# LogiTune
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "LogiTune.exe"
komorebic.exe float-rule exe "LogiTune.exe"
# Logitech G HUB
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "lghub.exe"
komorebic.exe identify-border-overflow-application exe "lghub.exe"
# Logitech Options
komorebic.exe float-rule exe "LogiOptionsUI.exe"
# Mailspring
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "mailspring.exe"
# ManyCam
komorebic.exe identify-border-overflow-application exe "ManyCam.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ManyCam.exe"
# Mica For Everyone
# Microsoft Excel
komorebic.exe identify-border-overflow-application exe "EXCEL.EXE"
komorebic.exe identify-layered-application exe "EXCEL.EXE"
# Targets a hidden window spawned by Microsoft Office applications
komorebic.exe float-rule class "_WwB"
# Microsoft Outlook
komorebic.exe identify-border-overflow-application exe "OUTLOOK.EXE"
komorebic.exe identify-layered-application exe "OUTLOOK.EXE"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "OUTLOOK.EXE"
# Microsoft PC Manager
komorebic.exe float-rule exe "MSPCManager.exe"
# Microsoft PowerPoint
komorebic.exe identify-border-overflow-application exe "POWERPNT.EXE"
komorebic.exe identify-layered-application exe "POWERPNT.EXE"
# Microsoft Teams
komorebic.exe identify-border-overflow-application exe "Teams.exe"
# Target Teams pop-up notification windows
komorebic.exe float-rule title "Microsoft Teams Notification"
# Target Teams call in progress windows
komorebic.exe float-rule title "Microsoft Teams Call"
# Microsoft Word
komorebic.exe identify-border-overflow-application exe "WINWORD.EXE"
komorebic.exe identify-layered-application exe "WINWORD.EXE"
# Modern Flyouts
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ModernFlyoutsHost.exe"
# Mozilla Firefox
komorebic.exe identify-object-name-change-application exe "firefox.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "firefox.exe"
# Targets invisible windows spawned by Firefox to show tab previews in the taskbar
komorebic.exe float-rule class "MozillaTaskbarPreviewClass"
# NVIDIA GeForce Experience
komorebic.exe identify-border-overflow-application exe "NVIDIA GeForce Experience.exe"
# NZXT CAM
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "NZXT CAM.exe"
# NiceHash Miner
komorebic.exe identify-border-overflow-application exe "nhm_app.exe"
komorebic.exe manage-rule exe "nhm_app.exe"
# NohBoard
komorebic.exe float-rule exe "NohBoard.exe"
# Notion Enhanced
komorebic.exe identify-border-overflow-application exe "Notion Enhanced.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Notion Enhanced.exe"
# OBS Studio (32-bit)
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "obs32.exe"
# OBS Studio (64-bit)
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "obs64.exe"
# ONLYOFFICE Editors
komorebic.exe identify-border-overflow-application class "DocEditorsWindowClass"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application class "DocEditorsWindowClass"
# Obsidian
komorebic.exe identify-border-overflow-application exe "Obsidian.exe"
komorebic.exe manage-rule exe "Obsidian.exe"
# OpenRGB
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "OpenRGB.exe"
# Paradox Launcher
komorebic.exe float-rule exe "Paradox Launcher.exe"
# Plexamp
komorebic.exe identify-border-overflow-application exe "Plexamp.exe"
# PowerToys
# Target color picker dialog
komorebic.exe float-rule exe "PowerToys.ColorPickerUI.exe"
# Target image resizer dialog
komorebic.exe float-rule exe "PowerToys.ImageResizer.exe"
# Process Hacker
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ProcessHacker.exe"
komorebic.exe float-rule exe "ProcessHacker.exe"
# ProtonVPN
komorebic.exe identify-border-overflow-application exe "ProtonVPN.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ProtonVPN.exe"
# PyCharm
komorebic.exe identify-object-name-change-application exe "pycharm64.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "pycharm64.exe"
# QtScrcpy
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "QtScrcpy.exe"
# QuickLook
komorebic.exe float-rule exe "QuickLook.exe"
# RepoZ
komorebic.exe float-rule exe "RepoZ.exe"
# Rider
komorebic.exe identify-object-name-change-application exe "rider64.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "rider64.exe"
# Roblox FPS Unlocker
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "rbxfpsunlocker.exe"
# RoundedTB
komorebic.exe float-rule exe "RoundedTB.exe"
# RoundedTB
komorebic.exe identify-border-overflow-application exe "RoundedTB.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "RoundedTB.exe"
# ShareX
komorebic.exe identify-border-overflow-application exe "ShareX.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ShareX.exe"
# Sideloadly
komorebic.exe float-rule exe "sideloadly.exe"
# Signal
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "signal.exe"
# SiriKali
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "sirikali.exe"
# Slack
komorebic.exe identify-border-overflow-application exe "Slack.exe"
komorebic.exe manage-rule exe "Slack.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Slack.exe"
# Slack
komorebic.exe identify-border-overflow-application exe "slack.exe"
komorebic.exe manage-rule exe "slack.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "slack.exe"
# Smart Install Maker
# Target hidden window spawned by installer
komorebic.exe float-rule class "obj_App"
# Target installer
komorebic.exe float-rule class "obj_Form"
# SoulseekQt
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "SoulseekQt.exe"
# Spotify
komorebic.exe identify-border-overflow-application exe "Spotify.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Spotify.exe"
# Steam
komorebic.exe identify-border-overflow-application class "vguiPopupWindow"
# Steam Beta
komorebic.exe identify-border-overflow-application class "SDL_app"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application class "SDL_app"
# Stremio
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "stremio.exe"
# System Informer
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "SystemInformer.exe"
komorebic.exe float-rule exe "SystemInformer.exe"
# SystemSettings
komorebic.exe float-rule class "Shell_Dialog"
# Task Manager
komorebic.exe float-rule class "TaskManagerWindow"
# Telegram
komorebic.exe identify-border-overflow-application exe "Telegram.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Telegram.exe"
# TickTick
komorebic.exe identify-border-overflow-application exe "TickTick.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "TickTick.exe"
# TouchCursor
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "tcconfig.exe"
komorebic.exe float-rule exe "tcconfig.exe"
# TranslucentTB
komorebic.exe float-rule exe "TranslucentTB.exe"
# TranslucentTB
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "TranslucentTB.exe"
# Unreal Editor
komorebic.exe identify-border-overflow-application exe "UnrealEditor.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "UnrealEditor.exe"
# VRCX
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "VRCX.exe"
# Visual Studio
komorebic.exe identify-object-name-change-application exe "devenv.exe"
# Visual Studio Code
komorebic.exe identify-border-overflow-application exe "Code.exe"
# Voice.ai
komorebic.exe identify-border-overflow-application exe "VoiceAI.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "VoiceAI.exe"
# WebTorrent Desktop
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "WebTorrent.exe"
# WinZip (32-bit)
komorebic.exe float-rule exe "winzip32.exe"
# WinZip (64-bit)
komorebic.exe float-rule exe "winzip64.exe"
# Windows Console (conhost.exe)
komorebic.exe manage-rule class "ConsoleWindowClass"
# Windows Explorer
# Targets copy/move operation windows
komorebic.exe float-rule class "OperationStatusWindow"
komorebic.exe float-rule title "Control Panel"
# Windows Installer
komorebic.exe float-rule exe "msiexec.exe"
# WingetUI
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "WingetUI.exe"
# WingetUI
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "wingetui.exe"
# Wox
# Targets a hidden window spawned by Wox
komorebic.exe float-rule title "Hotkey sink"
# XAMPP Control Panel
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "xampp-control.exe"
# Zoom
komorebic.exe float-rule exe "Zoom.exe"
# mpv.net
komorebic.exe identify-object-name-change-application exe "mpvnet.exe"
# paint.net
komorebic.exe float-rule exe "paintdotnet.exe"
# pinentry
komorebic.exe float-rule exe "pinentry.exe"
# qBittorrent
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "qbittorrent.exe"
# ueli
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "ueli.exe"
komorebic.exe float-rule exe "ueli.exe"

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="RUST_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/bindings/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/komorebi-core/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/komorebi/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/komorebic/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,225 +1,93 @@
#SingleInstance Force
; Load library
#Include komorebic.lib.ahk
; Load configuration
#Include komorebi.generated.ahk
; Send the ALT key whenever changing focus to force focus changes
AltFocusHack("enable")
; Default to cloaking windows when switching workspaces
WindowHidingBehaviour("cloak")
; Set cross-monitor move behaviour to insert instead of swap
CrossMonitorMoveBehaviour("Insert")
; Enable hot reloading of changes to this file
Run, komorebic.exe watch-configuration enable, , Hide
WatchConfiguration("enable")
; Create named workspaces I-V on monitor 0
EnsureNamedWorkspaces(0, "I II III IV V")
; You can do the same thing for secondary monitors too
; EnsureNamedWorkspaces(1, "A B C D E F")
; Assign layouts to workspaces, possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack
NamedWorkspaceLayout("I", "bsp")
; Set the gaps around the edge of the screen for a workspace
NamedWorkspacePadding("I", 20)
; Set the gaps between the containers for a workspace
NamedWorkspaceContainerPadding("I", 20)
; You can assign specific apps to named workspaces
; NamedWorkspaceRule("exe", "Firefox.exe", "III")
; Configure the invisible border dimensions
Run, komorebic.exe invisible-borders 7 0 14 7, , Hide
InvisibleBorders(7, 0, 14, 7)
; Enable focus follows mouse
Run, komorebic.exe focus-follows-mouse enable, , Hide
; Uncomment the next lines if you want a visual border around the active window
; ActiveWindowBorderColour(66, 165, 245, "single")
; ActiveWindowBorderColour(256, 165, 66, "stack")
; ActiveWindowBorderColour(255, 51, 153, "monocle")
; Ensure there are 3 workspaces created on monitor 0
Run, komorebic.exe ensure-workspaces 0 5, , Hide
CompleteConfiguration()
; Give the workspaces some optional names
Run, komorebic.exe workspace-name 0 0 bsp, , Hide
Run, komorebic.exe workspace-name 0 1 columns, , Hide
Run, komorebic.exe workspace-name 0 2 thicc, , Hide
Run, komorebic.exe workspace-name 0 3 matrix, , Hide
Run, komorebic.exe workspace-name 0 4 floaty, , Hide
; Focus windows
!h::Focus("left")
!j::Focus("down")
!k::Focus("up")
!l::Focus("right")
!+[::CycleFocus("previous")
!+]::CycleFocus("next")
; Set the padding of the different workspaces
Run, komorebic.exe workspace-padding 0 1 30, , Hide
Run, komorebic.exe container-padding 0 1 30, , Hide
Run, komorebic.exe workspace-padding 0 2 200, , Hide
Run, komorebic.exe workspace-padding 0 3 0, , Hide
Run, komorebic.exe container-padding 0 3 0, , Hide
; Move windows
!+h::Move("left")
!+j::Move("down")
!+k::Move("up")
!+l::Move("right")
!+Enter::Promote()
; Set the layouts of different workspaces
Run, komorebic.exe workspace-layout 0 1 columns, , Hide
; Stack windows
!Left::Stack("left")
!Right::Stack("right")
!Up::Stack("up")
!Down::Stack("down")
!;::Unstack()
![::CycleStack("previous")
!]::CycleStack("next")
; Set the floaty layout to not tile any windows
Run, komorebic.exe workspace-tiling 0 4 disable, , Hide
; Resize
!=::ResizeAxis("horizontal", "increase")
!-::ResizeAxis("horizontal", "decrease")
!+=::ResizeAxis("vertical", "increase")
!+-::ResizeAxis("vertical", "decrease")
; Always show chat apps on the second workspace
Run, komorebic.exe workspace-rule exe slack.exe 0 1, , Hide
Run, komorebic.exe workspace-rule exe Discord.exe 0 1, , Hide
; Manipulate windows
!t::ToggleFloat()
!+f::ToggleMonocle()
; Always float IntelliJ popups, matching on class
Run, komorebic.exe float-rule class SunAwtDialog, , Hide
; Always float Control Panel, matching on title
Run, komorebic.exe float-rule title "Control Panel", , Hide
; Always float Task Manager, matching on class
Run, komorebic.exe float-rule class TaskManagerWindow, , Hide
; Always float Wally, matching on executable name
Run, komorebic.exe float-rule exe Wally.exe, , Hide
Run, komorebic.exe float-rule exe wincompose.exe, , Hide
; Always float Calculator app, matching on window title
Run, komorebic.exe float-rule title Calculator, , Hide
Run, komorebic.exe float-rule exe 1Password.exe, , Hide
; Window manager options
!+r::Retile()
!p::TogglePause()
; Always manage forcibly these applications that don't automatically get picked up by komorebi
Run, komorebic.exe manage-rule exe TIM.exe, , Hide
; Layouts
!x::FlipLayout("horizontal")
!y::FlipLayout("vertical")
; Identify applications that close to the tray
Run, komorebic.exe identify-tray-application exe Discord.exe, , Hide
; Workspaces
!1::FocusWorkspace(0)
!2::FocusWorkspace(1)
!3::FocusWorkspace(2)
; Identify applications that have overflowing borders
Run, komorebic.exe identify-border-overflow exe Discord.exe, , Hide
; Change the focused window, Alt + Vim direction keys
!h::
Run, komorebic.exe focus left, , Hide
return
!j::
Run, komorebic.exe focus down, , Hide
return
!k::
Run, komorebic.exe focus up, , Hide
return
!l::
Run, komorebic.exe focus right, , Hide
return
; Move the focused window in a given direction, Alt + Shift + Vim direction keys
!+h::
Run, komorebic.exe move left, , Hide
return
!+j::
Run, komorebic.exe move down, , Hide
return
!+k::
Run, komorebic.exe move up, , Hide
return
!+l::
Run, komorebic.exe move right, , Hide
return
; Stack the focused window in a given direction, Alt + Shift + direction keys
!+Left::
Run, komorebic.exe stack left, , Hide
return
!+Down::
Run, komorebic.exe stack down, , Hide
return
!+Up::
Run, komorebic.exe stack up, , Hide
return
!+Right::
Run, komorebic.exe stack right, , Hide
return
!]::
Run, komorebic.exe cycle-stack next, , Hide
return
![::
Run, komorebic.exe cycle-stack previous, , Hide
return
; Unstack the focused window, Alt + Shift + D
!+d::
Run, komorebic.exe unstack, , Hide
return
; Promote the focused window to the top of the tree, Alt + Shift + Enter
!+Enter::
Run, komorebic.exe promote, , Hide
return
; Switch to an equal-width, max-height column layout on the main workspace, Alt + Shift + C
!+c::
Run, komorebic.exe workspace-layout 0 0 columns, , Hide
return
; Switch to the default bsp tiling layout on the main workspace, Alt + Shift + T
!+t::
Run, komorebic.exe workspace-layout 0 0 bsp, , Hide
return
; Toggle the Monocle layout for the focused window, Alt + Shift + F
!+f::
Run, komorebic.exe toggle-monocle, , Hide
return
; Toggle native maximize for the focused window, Alt + Shift + =
!+=::
Run, komorebic.exe toggle-maximize, , Hide
return
; Flip horizontally, Alt + X
!x::
Run, komorebic.exe flip-layout horizontal, , Hide
return
; Flip vertically, Alt + Y
!y::
Run, komorebic.exe flip-layout vertical, , Hide
return
; Force a retile if things get janky, Alt + Shift + R
!+r::
Run, komorebic.exe retile, , Hide
return
; Float the focused window, Alt + T
!t::
Run, komorebic.exe toggle-float, , Hide
return
; Reload ~/komorebi.ahk, Alt + O
!o::
Run, komorebic.exe reload-configuration, , Hide
return
; Pause responding to any window events or komorebic commands, Alt + P
!p::
Run, komorebic.exe toggle-pause, , Hide
return
; Switch to workspace
!1::
Send !
Run, komorebic.exe focus-workspace 0, , Hide
return
!2::
Send !
Run, komorebic.exe focus-workspace 1, , Hide
return
!3::
Send !
Run, komorebic.exe focus-workspace 2, , Hide
return
!4::
Send !
Run, komorebic.exe focus-workspace 3, , Hide
return
!5::
Send !
Run, komorebic.exe focus-workspace 4, , Hide
return
; Move window to workspace
!+1::
Run, komorebic.exe move-to-workspace 0, , Hide
return
!+2::
Run, komorebic.exe move-to-workspace 1, , Hide
return
!+3::
Run, komorebic.exe move-to-workspace 2, , Hide
return
!+4::
Run, komorebic.exe move-to-workspace 3, , Hide
return
!+5::
Run, komorebic.exe move-to-workspace 4, , Hide
return
; Move windows across workspaces
!+1::MoveToWorkspace(0)
!+2::MoveToWorkspace(1)
!+3::MoveToWorkspace(2)

42
komorebi.sample.ps1 Normal file
View File

@@ -0,0 +1,42 @@
if (!(Get-Process whkd -ErrorAction SilentlyContinue))
{
Start-Process whkd -WindowStyle hidden
}
. $PSScriptRoot\komorebi.generated.ps1
# Send the ALT key whenever changing focus to force focus changes
komorebic alt-focus-hack enable
# Default to cloaking windows when switching workspaces
komorebic window-hiding-behaviour cloak
# Set cross-monitor move behaviour to insert instead of swap
komorebic cross-monitor-move-behaviour insert
# Enable hot reloading of changes to this file
komorebic watch-configuration enable
# Create named workspaces I-V on monitor 0
komorebic ensure-named-workspaces 0 I II III IV V
# You can do the same thing for secondary monitors too
# komorebic ensure-named-workspaces 1 A B C D E F
# Assign layouts to workspaces, possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack
komorebic named-workspace-layout I bsp
# Set the gaps around the edge of the screen for a workspace
komorebic named-workspace-padding I 20
# Set the gaps between the containers for a workspace
komorebic named-workspace-container-padding I 20
# You can assign specific apps to named workspaces
# komorebic named-workspace-rule exe "Firefox.exe" III
# Configure the invisible border dimensions
komorebic invisible-borders 7 0 14 7
# Uncomment the next lines if you want a visual border around the active window
# komorebic active-window-border-colour 66 165 245 --window-kind single
# komorebic active-window-border-colour 256 165 66 --window-kind stack
# komorebic active-window-border-colour 255 51 153 --window-kind monocle
# komorebic active-window-border enable
komorebic complete-configuration

View File

@@ -1,243 +0,0 @@
#SingleInstance Force
#Include %A_ScriptDir%\komorebic.lib.ahk
; Default to minimizing windows when switching workspaces
WindowHidingBehaviour("minimize")
; Enable hot reloading of changes to this file
WatchConfiguration("enable")
; Ensure there are 5 workspaces created on monitor 0
EnsureWorkspaces(0, 5)
; Configure the invisible border dimensions
InvisibleBorders(7, 0, 14, 7)
; Configure the 1st workspace
WorkspaceName(0, 0, "bsp")
; Configure the 2nd workspace
WorkspaceName(0, 1, "columns") ; Optionally set the name of the workspace
WorkspacePadding(0, 1, 30) ; Set the padding around the edge of the screen
ContainerPadding(0, 1, 30) ; Set the padding between the containers on the screen
WorkspaceRule("exe", "slack.exe", 0, 1) ; Always show chat apps on this workspace
; Configure the 3rd workspace
WorkspaceName(0, 2, "thicc")
WorkspacePadding(0, 2, 200) ; Set some super thicc padding
; Configure the 4th workspace
WorkspaceName(0, 3, "matrix")
WorkspacePadding(0, 3, 0) ; No padding at all
ContainerPadding(0, 3, 0) ; Matrix-y hacker vibes
; Configure the 5th workspace
WorkspaceName(0, 4, "floaty")
WorkspaceTiling(0, 4, "disable") ; Everything floats here
; Configure floating rules
FloatRule("class", "SunAwtDialog") ; All the IntelliJ popups
FloatRule("title", "Control Panel")
FloatRule("class", "TaskManagerWindow")
FloatRule("exe", "Wally.exe")
FloatRule("exe", "wincompose.exe")
FloatRule("exe", "1Password.exe")
FloatRule("exe", "Wox.exe")
FloatRule("exe", "ddm.exe")
FloatRule("class", "Chrome_RenderWidgetHostHWND") ; GOG Electron invisible overlay
FloatRule("class", "CEFCLIENT")
; Identify Minimize-to-Tray Applications
IdentifyTrayApplication("exe", "Discord.exe")
IdentifyTrayApplication("exe", "Spotify.exe")
IdentifyTrayApplication("exe", "GalaxyClient.exe")
; Identify Electron applications with overflowing borders
IdentifyBorderOverflow("exe", "Discord.exe")
IdentifyBorderOverflow("exe", "Spotify.exe")
IdentifyBorderOverflow("exe", "GalaxyClient.exe")
IdentifyBorderOverflow("class", "ZPFTEWndClass")
; Identify applications to be forcibly managed
ManageRule("exe", "GalaxyClient.exe")
; Change the focused window, Alt + Vim direction keys
!h::
Focus("left")
return
!j::
Focus("down")
return
!k::
Focus("up")
return
!l::
Focus("right")
return
; Move the focused window in a given direction, Alt + Shift + Vim direction keys
!+h::
Move("left")
return
!+j::
Move("down")
return
!+k::
Move("up")
return
!+l::
Move("right")
return
; Stack the focused window in a given direction, Alt + Shift + direction keys
!+Left::
Stack("left")
return
!+Down::
Stack("down")
return
!+Up::
Stack("up")
return
!+Right::
Stack("right")
return
!]::
CycleStack("next")
return
![::
CycleStack("previous")
return
; Unstack the focused window, Alt + Shift + D
!+d::
Unstack()
return
; Promote the focused window to the top of the tree, Alt + Shift + Enter
!+Enter::
Promote()
return
; Manage the focused window
!=::
Manage()
return
; Unmanage the focused window
!-::
Unmanage()
return
; Switch to an equal-width, max-height column layout on the main workspace, Alt + Shift + C
!+c::
ChangeLayout("columns")
return
; Switch to the default bsp tiling layout on the main workspace, Alt + Shift + T
!+t::
ChangeLayout("bsp")
return
; Toggle the Monocle layout for the focused window, Alt + Shift + F
!+f::
ToggleMonocle()
return
; Toggle native maximize for the focused window, Alt + Shift + =
!+=::
ToggleMaximize()
return
; Flip horizontally, Alt + X
!x::
FlipLayout("horizontal")
return
; Flip vertically, Alt + Y
!y::
FlipLayout("vertical")
return
; Force a retile if things get janky, Alt + Shift + R
!+r::
Retile()
return
; Float the focused window, Alt + T
!t::
ToggleFloat()
return
; Reload ~/komorebi.ahk, Alt + O
!o::
ReloadConfiguration()
return
; Pause responding to any window events or komorebic commands, Alt + P
!p::
TogglePause()
return
; Enable focus follows mouse
!0::
ToggleFocusFollowsMouse("komorebi")
return
; Switch to workspace
!1::
Send !
FocusWorkspace(0)
return
!2::
Send !
FocusWorkspace(1)
return
!3::
Send !
FocusWorkspace(2)
return
!4::
Send !
FocusWorkspace(3)
return
!5::
Send !
FocusWorkspace(4)
return
; Move window to workspace
!+1::
MoveToWorkspace(0)
return
!+2::
MoveToWorkspace(1)
return
!+3::
MoveToWorkspace(2)
return
!+4::
MoveToWorkspace(3)
return
!+5::
MoveToWorkspace(4)
return

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.12"
version = "0.1.16"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -13,37 +13,42 @@ edition = "2021"
[dependencies]
komorebi-core = { path = "../komorebi-core" }
bitflags = "1"
clap = { version = "3", features = ["derive"] }
bitflags = "2"
clap = { version = "4", features = ["derive"] }
color-eyre = "0.6"
crossbeam-channel = "0.5"
crossbeam-utils = "0.8"
ctrlc = "3"
dirs = "4"
dirs = "5"
getset = "0.1"
hotwatch = "0.4"
lazy_static = "1"
miow = "0.4"
miow = "0.5"
nanoid = "0.4"
os_info = "3.4"
net2 = "0.2"
os_info = "3.7"
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.29"
tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uds_windows = "1"
which = "4"
winput = "0.2"
winreg = "0.10"
winreg = "0.50"
windows-interface = { version = "0.48" }
windows-implement = { version = "0.48" }
[dependencies.windows]
version = "0.39"
version = "0.48"
features = [
"implement",
"Win32_System_Com",
"Win32_UI_Shell_Common", # for IObjectArray
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
@@ -55,7 +60,8 @@ features = [
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_Shell_Common",
"Win32_UI_WindowsAndMessaging"
"Win32_UI_WindowsAndMessaging",
"Win32_System_SystemServices"
]
[features]

View File

@@ -2,7 +2,6 @@ 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;
@@ -13,11 +12,16 @@ use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use komorebi_core::Rect;
use crate::window::Window;
use crate::windows_callbacks;
use crate::WindowsApi;
use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_RECT;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_11;
#[derive(Debug, Clone, Copy)]
@@ -40,7 +44,7 @@ impl Border {
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 brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
let window_class = WNDCLASSA {
hInstance: instance,
lpszClassName: class_name,
@@ -84,7 +88,11 @@ impl Border {
}
pub fn hide(self) -> Result<()> {
WindowsApi::hide_border_window(self.hwnd())
if self.hwnd == 0 {
Ok(())
} else {
WindowsApi::hide_border_window(self.hwnd())
}
}
pub fn set_position(
@@ -93,27 +101,45 @@ impl Border {
invisible_borders: &Rect,
activate: bool,
) -> Result<()> {
let mut should_expand_border = false;
if self.hwnd == 0 {
Ok(())
} else {
if !WindowsApi::is_window(self.hwnd()) {
Self::create("komorebi-border-window")?;
}
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= invisible_borders.bottom;
rect.bottom += invisible_borders.bottom;
let mut should_expand_border = false;
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;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= invisible_borders.bottom;
rect.bottom += invisible_borders.bottom;
}
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
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;
}
let border_offset = BORDER_OFFSET.lock();
if let Some(border_offset) = *border_offset {
rect.left -= border_offset.left;
rect.top -= border_offset.top;
rect.right += border_offset.right;
rect.bottom += border_offset.bottom;
}
*BORDER_RECT.lock() = rect;
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
}
}
}

View File

@@ -0,0 +1,246 @@
// This code is largely taken verbatim from this repository: https://github.com/Ciantic/AltTabAccessor
// which the author Jari Pennanen (Ciantic) has kindly made available with the MIT license, available
// in full here: https://github.com/Ciantic/AltTabAccessor/blob/main/LICENSE.txt
#![allow(clippy::use_self)]
use std::ffi::c_void;
use std::ops::Deref;
use windows::core::IUnknown;
use windows::core::IUnknown_Vtbl;
use windows::core::GUID;
use windows::core::HRESULT;
use windows::core::HSTRING;
use windows::core::PCWSTR;
use windows::core::PWSTR;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::SIZE;
use windows::Win32::UI::Shell::Common::IObjectArray;
type DesktopID = GUID;
// Idea here is that the cloned ComIn instance lifetime is within the original ComIn instance lifetime
#[repr(transparent)]
pub struct ComIn<'a, T> {
data: T,
_phantom: std::marker::PhantomData<&'a T>,
}
impl<'a, T: Clone> ComIn<'a, T> {
pub fn new(t: &'a T) -> Self {
Self {
data: t.clone(),
_phantom: std::marker::PhantomData,
}
}
pub const unsafe fn unsafe_new_no_clone(t: T) -> Self {
Self {
data: t,
_phantom: std::marker::PhantomData,
}
}
}
impl<'a, T> Deref for ComIn<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
#[allow(non_upper_case_globals)]
pub const CLSID_ImmersiveShell: GUID = GUID {
data1: 0xC2F0_3A33,
data2: 0x21F5,
data3: 0x47FA,
data4: [0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39],
};
#[allow(clippy::upper_case_acronyms)]
type DWORD = u32;
#[allow(clippy::upper_case_acronyms)]
type INT = i32;
#[allow(clippy::upper_case_acronyms)]
type LPVOID = *mut c_void;
#[allow(clippy::upper_case_acronyms)]
type UINT = u32;
#[allow(clippy::upper_case_acronyms)]
type ULONG = u32;
#[allow(clippy::upper_case_acronyms)]
type ULONGLONG = u64;
type IAsyncCallback = UINT;
type IImmersiveMonitor = UINT;
type IApplicationViewOperation = UINT;
type IApplicationViewPosition = UINT;
type IImmersiveApplication = UINT;
type IApplicationViewChangeListener = UINT;
#[allow(non_camel_case_types)]
type APPLICATION_VIEW_COMPATIBILITY_POLICY = UINT;
#[allow(non_camel_case_types)]
type APPLICATION_VIEW_CLOAK_TYPE = UINT;
#[windows_interface::interface("6D5140C1-7436-11CE-8034-00AA006009FA")]
pub unsafe trait IServiceProvider: IUnknown {
pub unsafe fn query_service(
&self,
guid_service: *const GUID,
riid: *const GUID,
ppv_object: *mut *mut c_void,
) -> HRESULT;
}
#[windows_interface::interface("372E1D3B-38D3-42E4-A15B-8AB2B178F513")]
pub unsafe trait IApplicationView: IUnknown {
/* IInspecateble */
pub unsafe fn get_iids(
&self,
out_iid_count: *mut ULONG,
out_opt_iid_array_ptr: *mut *mut GUID,
) -> HRESULT;
pub unsafe fn get_runtime_class_name(&self, out_opt_class_name: *mut HSTRING) -> HRESULT;
pub unsafe fn get_trust_level(&self, ptr_trust_level: LPVOID) -> HRESULT;
/* IApplicationView methods */
pub unsafe fn set_focus(&self) -> HRESULT;
pub unsafe fn switch_to(&self) -> HRESULT;
pub unsafe fn try_invoke_back(&self, ptr_async_callback: IAsyncCallback) -> HRESULT;
pub unsafe fn get_thumbnail_window(&self, out_hwnd: *mut HWND) -> HRESULT;
pub unsafe fn get_monitor(&self, out_monitors: *mut *mut IImmersiveMonitor) -> HRESULT;
pub unsafe fn get_visibility(&self, out_int: LPVOID) -> HRESULT;
pub unsafe fn set_cloak(
&self,
application_view_cloak_type: APPLICATION_VIEW_CLOAK_TYPE,
unknown: INT,
) -> HRESULT;
pub unsafe fn get_position(
&self,
unknowniid: *const GUID,
unknown_array_ptr: LPVOID,
) -> HRESULT;
pub unsafe fn set_position(&self, view_position: *mut IApplicationViewPosition) -> HRESULT;
pub unsafe fn insert_after_window(&self, window: HWND) -> HRESULT;
pub unsafe fn get_extended_frame_position(&self, rect: *mut RECT) -> HRESULT;
pub unsafe fn get_app_user_model_id(&self, id: *mut PWSTR) -> HRESULT; // Proc17
pub unsafe fn set_app_user_model_id(&self, id: PCWSTR) -> HRESULT;
pub unsafe fn is_equal_by_app_user_model_id(&self, id: PCWSTR, out_result: *mut INT)
-> HRESULT;
/*** IApplicationView methods ***/
pub unsafe fn get_view_state(&self, out_state: *mut UINT) -> HRESULT; // Proc20
pub unsafe fn set_view_state(&self, state: UINT) -> HRESULT; // Proc21
pub unsafe fn get_neediness(&self, out_neediness: *mut INT) -> HRESULT; // Proc22
pub unsafe fn get_last_activation_timestamp(&self, out_timestamp: *mut ULONGLONG) -> HRESULT;
pub unsafe fn set_last_activation_timestamp(&self, timestamp: ULONGLONG) -> HRESULT;
pub unsafe fn get_virtual_desktop_id(&self, out_desktop_guid: *mut DesktopID) -> HRESULT;
pub unsafe fn set_virtual_desktop_id(&self, desktop_guid: *const DesktopID) -> HRESULT;
pub unsafe fn get_show_in_switchers(&self, out_show: *mut INT) -> HRESULT;
pub unsafe fn set_show_in_switchers(&self, show: INT) -> HRESULT;
pub unsafe fn get_scale_factor(&self, out_scale_factor: *mut INT) -> HRESULT;
pub unsafe fn can_receive_input(&self, out_can: *mut BOOL) -> HRESULT;
pub unsafe fn get_compatibility_policy_type(
&self,
out_policy_type: *mut APPLICATION_VIEW_COMPATIBILITY_POLICY,
) -> HRESULT;
pub unsafe fn set_compatibility_policy_type(
&self,
policy_type: APPLICATION_VIEW_COMPATIBILITY_POLICY,
) -> HRESULT;
pub unsafe fn get_size_constraints(
&self,
monitor: *mut IImmersiveMonitor,
out_size1: *mut SIZE,
out_size2: *mut SIZE,
) -> HRESULT;
pub unsafe fn get_size_constraints_for_dpi(
&self,
dpi: UINT,
out_size1: *mut SIZE,
out_size2: *mut SIZE,
) -> HRESULT;
pub unsafe fn set_size_constraints_for_dpi(
&self,
dpi: *const UINT,
size1: *const SIZE,
size2: *const SIZE,
) -> HRESULT;
pub unsafe fn on_min_size_preferences_updated(&self, window: HWND) -> HRESULT;
pub unsafe fn apply_operation(&self, operation: *mut IApplicationViewOperation) -> HRESULT;
pub unsafe fn is_tray(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn is_in_high_zorder_band(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn is_splash_screen_presented(&self, out_is: *mut BOOL) -> HRESULT;
pub unsafe fn flash(&self) -> HRESULT;
pub unsafe fn get_root_switchable_owner(&self, app_view: *mut IApplicationView) -> HRESULT; // proc45
pub unsafe fn enumerate_ownership_tree(&self, objects: *mut IObjectArray) -> HRESULT; // proc46
pub unsafe fn get_enterprise_id(&self, out_id: *mut PWSTR) -> HRESULT; // proc47
pub unsafe fn is_mirrored(&self, out_is: *mut BOOL) -> HRESULT; //
pub unsafe fn unknown1(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown2(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown3(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown4(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown5(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown6(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown7(&self) -> HRESULT;
pub unsafe fn unknown8(&self, arg: *mut INT) -> HRESULT;
pub unsafe fn unknown9(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown10(&self, arg: INT, arg2: INT) -> HRESULT;
pub unsafe fn unknown11(&self, arg: INT) -> HRESULT;
pub unsafe fn unknown12(&self, arg: *mut SIZE) -> HRESULT;
}
#[windows_interface::interface("1841c6d7-4f9d-42c0-af41-8747538f10e5")]
pub unsafe trait IApplicationViewCollection: IUnknown {
pub unsafe fn get_views(&self, out_views: *mut IObjectArray) -> HRESULT;
pub unsafe fn get_views_by_zorder(&self, out_views: *mut IObjectArray) -> HRESULT;
pub unsafe fn get_views_by_app_user_model_id(
&self,
id: PCWSTR,
out_views: *mut IObjectArray,
) -> HRESULT;
pub unsafe fn get_view_for_hwnd(
&self,
window: HWND,
out_view: *mut Option<IApplicationView>,
) -> HRESULT;
pub unsafe fn get_view_for_application(
&self,
app: ComIn<IImmersiveApplication>,
out_view: *mut IApplicationView,
) -> HRESULT;
pub unsafe fn get_view_for_app_user_model_id(
&self,
id: PCWSTR,
out_view: *mut IApplicationView,
) -> HRESULT;
pub unsafe fn get_view_in_focus(&self, out_view: *mut IApplicationView) -> HRESULT;
pub unsafe fn try_get_last_active_visible_view(
&self,
out_view: *mut IApplicationView,
) -> HRESULT;
pub unsafe fn refresh_collection(&self) -> HRESULT;
pub unsafe fn register_for_application_view_changes(
&self,
listener: ComIn<IApplicationViewChangeListener>,
out_id: *mut DWORD,
) -> HRESULT;
pub unsafe fn unregister_for_application_view_changes(&self, id: DWORD) -> HRESULT;
}

104
komorebi/src/com/mod.rs Normal file
View File

@@ -0,0 +1,104 @@
// This code is largely taken verbatim from this repository: https://github.com/Ciantic/AltTabAccessor
// which the author Jari Pennanen (Ciantic) has kindly made available with the MIT license, available
// in full here: https://github.com/Ciantic/AltTabAccessor/blob/main/LICENSE.txt
mod interfaces;
use interfaces::CLSID_ImmersiveShell;
use interfaces::IApplicationViewCollection;
use interfaces::IServiceProvider;
use std::ffi::c_void;
use windows::core::ComInterface;
use windows::core::Interface;
use windows::Win32::Foundation::HWND;
use windows::Win32::System::Com::CoCreateInstance;
use windows::Win32::System::Com::CoInitializeEx;
use windows::Win32::System::Com::CoUninitialize;
use windows::Win32::System::Com::CLSCTX_ALL;
use windows::Win32::System::Com::COINIT_APARTMENTTHREADED;
struct ComInit();
impl ComInit {
pub fn new() -> Self {
unsafe {
// Notice: Only COINIT_APARTMENTTHREADED works correctly!
//
// Not COINIT_MULTITHREADED or CoIncrementMTAUsage, they cause a seldom crashes in threading tests.
CoInitializeEx(None, COINIT_APARTMENTTHREADED).unwrap();
}
Self()
}
}
impl Drop for ComInit {
fn drop(&mut self) {
unsafe {
CoUninitialize();
}
}
}
thread_local! {
static COM_INIT: ComInit = ComInit::new();
}
fn get_iservice_provider() -> IServiceProvider {
COM_INIT.with(|_| unsafe { CoCreateInstance(&CLSID_ImmersiveShell, None, CLSCTX_ALL).unwrap() })
}
fn get_iapplication_view_collection(provider: &IServiceProvider) -> IApplicationViewCollection {
COM_INIT.with(|_| {
let mut obj = std::ptr::null_mut::<c_void>();
unsafe {
provider
.query_service(
&IApplicationViewCollection::IID,
&IApplicationViewCollection::IID,
&mut obj,
)
.unwrap();
}
assert!(!obj.is_null());
unsafe { IApplicationViewCollection::from_raw(obj) }
})
}
#[no_mangle]
pub extern "C" fn SetCloak(hwnd: HWND, cloak_type: u32, flags: i32) {
COM_INIT.with(|_| {
let provider = get_iservice_provider();
let view_collection = get_iapplication_view_collection(&provider);
let mut view = None;
unsafe {
if view_collection.get_view_for_hwnd(hwnd, &mut view).is_err() {
tracing::error!(
"could not get view for hwnd {} due to os error: {}",
hwnd.0,
std::io::Error::last_os_error()
);
}
};
view.map_or_else(
|| {
tracing::error!("no view was found for {}", hwnd.0,);
},
|view| {
unsafe {
if view.set_cloak(cloak_type, flags).is_err() {
tracing::error!(
"could not change the cloaking status for hwnd {} due to os error: {}",
hwnd.0,
std::io::Error::last_os_error()
);
}
};
},
);
});
}

78
komorebi/src/hidden.rs Normal file
View File

@@ -0,0 +1,78 @@
use std::sync::atomic::Ordering;
use std::time::Duration;
use color_eyre::Result;
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::windows_callbacks;
use crate::WindowsApi;
use crate::HIDDEN_HWND;
use crate::TRANSPARENCY_COLOUR;
#[derive(Debug, Clone, Copy)]
pub struct Hidden {
pub(crate) hwnd: isize,
}
impl From<isize> for Hidden {
fn from(hwnd: isize) -> Self {
Self { hwnd }
}
}
impl Hidden {
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(TRANSPARENCY_COLOUR);
let window_class = WNDCLASSA {
hInstance: instance,
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(windows_callbacks::hidden_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_hidden_window(PCSTR(name_cl.as_ptr()), instance)?;
let hidden = Self::from(hwnd);
let mut message = MSG::default();
unsafe {
while GetMessageA(&mut message, hidden.hwnd(), 0, 0).into() {
DispatchMessageA(&message);
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()) };
}
HIDDEN_HWND.store(hwnd.0, Ordering::SeqCst);
Ok(())
}
}

View File

@@ -4,9 +4,11 @@
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::net::TcpStream;
use std::path::PathBuf;
use std::process::Command;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicIsize;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
@@ -37,11 +39,13 @@ use which::which;
use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey;
use crate::hidden::Hidden;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
use komorebi_core::SocketMessage;
use crate::border::Border;
use crate::process_command::listen_for_commands;
use crate::process_command::listen_for_commands_tcp;
use crate::process_event::listen_for_events;
use crate::process_movement::listen_for_movements;
use crate::window_manager::State;
@@ -53,7 +57,9 @@ use crate::windows_api::WindowsApi;
mod ring;
mod border;
mod com;
mod container;
mod hidden;
mod monitor;
mod process_command;
mod process_event;
@@ -86,7 +92,9 @@ lazy_static! {
"firefox.exe".to_string(),
"idea64.exe".to_string(),
]));
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, (usize, usize)>>> =
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, (usize, usize, bool)>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
@@ -98,15 +106,16 @@ lazy_static! {
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(),
"mstsc.exe".to_string(),
"vcxsrv.exe".to_string(),
]));
static ref SUBSCRIPTION_PIPES: Arc<Mutex<HashMap<String, File>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref TCP_CONNECTIONS: Arc<Mutex<HashMap<String, TcpStream>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref HIDING_BEHAVIOUR: Arc<Mutex<HidingBehaviour>> =
Arc::new(Mutex::new(HidingBehaviour::Minimize));
static ref HOME_DIR: PathBuf = {
if let Ok(home_path) = std::env::var("KOMOREBI_CONFIG_HOME") {
std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(|_| dirs::home_dir().expect("there is no home directory"), |home_path| {
let home = PathBuf::from(&home_path);
if home.as_path().is_dir() {
@@ -117,48 +126,50 @@ lazy_static! {
home_path
);
}
} else {
dirs::home_dir().expect("there is no home directory")
}
})
};
static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
static ref AHK_V1_EXE: String = {
let mut ahk_v1: String = String::from("autohotkey.exe");
static ref AHK_EXE: String = {
let mut ahk: String = String::from("autohotkey.exe");
if let Ok(komorebi_ahk_v1_exe) = std::env::var("KOMOREBI_AHK_V1_EXE") {
if which(&komorebi_ahk_v1_exe).is_ok() {
ahk_v1 = komorebi_ahk_v1_exe;
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE") {
if which(&komorebi_ahk_exe).is_ok() {
ahk = komorebi_ahk_exe;
}
}
ahk_v1
ahk
};
static ref AHK_V2_EXE: String = {
let mut ahk_v2: String = String::from("AutoHotkey64.exe");
if let Ok(komorebi_ahk_v2_exe) = std::env::var("KOMOREBI_AHK_V2_EXE") {
if which(&komorebi_ahk_v2_exe).is_ok() {
ahk_v2 = komorebi_ahk_v2_exe;
}
}
ahk_v2
};
static ref WINDOWS_11: bool = {
matches!(
os_info::get().version(),
Version::Semantic(_, _, x) if x >= &22000
)
};
static ref BORDER_RECT: Arc<Mutex<Rect>> =
Arc::new(Mutex::new(Rect::default()));
static ref BORDER_OFFSET: Arc<Mutex<Option<Rect>>> =
Arc::new(Mutex::new(None));
}
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 ALT_FOCUS_HACK: AtomicBool = AtomicBool::new(false);
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
pub static BORDER_COLOUR_SINGLE: AtomicU32 = AtomicU32::new(0);
pub static BORDER_COLOUR_STACK: AtomicU32 = AtomicU32::new(0);
pub static BORDER_COLOUR_MONOCLE: AtomicU32 = AtomicU32::new(0);
pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
pub const TRANSPARENCY_COLOUR: u32 = 0;
pub static HIDDEN_HWND: AtomicIsize = AtomicIsize::new(0);
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
@@ -226,41 +237,48 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
pub fn load_configuration() -> Result<()> {
let home = HOME_DIR.clone();
let mut config_v1 = home.clone();
config_v1.push("komorebi.ahk");
let mut config_pwsh = home.clone();
config_pwsh.push("komorebi.ps1");
let mut config_v2 = home;
config_v2.push("komorebi.ahk2");
let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
if config_pwsh.exists() {
let powershell_exe = if which("pwsh.exe").is_ok() {
"pwsh.exe"
} else {
"powershell.exe"
};
if config_v1.exists() && which(&*AHK_V1_EXE).is_ok() {
tracing::info!(
"loading configuration file: {}",
config_v1
config_pwsh
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
Command::new(powershell_exe)
.arg(config_pwsh.as_os_str())
.output()?;
} else if config_ahk.exists() && which(&*AHK_EXE).is_ok() {
tracing::info!(
"loading configuration file: {}",
config_ahk
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
Command::new("autohotkey.exe")
.arg(config_v1.as_os_str())
.output()?;
} else if config_v2.exists() && which(&*AHK_V2_EXE).is_ok() {
tracing::info!(
"loading configuration file: {}",
config_v2
.as_os_str()
.to_str()
.ok_or_else(|| anyhow!("cannot convert path to string"))?
);
Command::new("AutoHotkey64.exe")
.arg(config_v2.as_os_str())
.arg(config_ahk.as_os_str())
.output()?;
}
Ok(())
}
#[must_use]
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
@@ -383,19 +401,33 @@ struct Opts {
/// Wait for 'komorebic complete-configuration' to be sent before processing events
#[clap(action, short, long)]
await_configuration: bool,
/// Start a TCP server on the given port to allow the direct sending of SocketMessages
#[clap(action, short, long)]
tcp_port: Option<usize>,
}
#[tracing::instrument]
#[allow(clippy::nonminimal_bool)]
fn main() -> Result<()> {
let opts: Opts = Opts::parse();
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);
|| (arg_count == 2
&& (opts.await_configuration || opts.focus_follows_mouse || opts.tcp_port.is_some()))
|| (arg_count == 3 && opts.await_configuration && opts.focus_follows_mouse)
|| (arg_count == 3 && opts.tcp_port.is_some() && opts.focus_follows_mouse)
|| (arg_count == 3 && opts.tcp_port.is_some() && opts.await_configuration)
|| (arg_count == 4
&& (opts.focus_follows_mouse && opts.await_configuration && opts.tcp_port.is_some()));
if has_valid_args {
let process_id = WindowsApi::current_process_id();
WindowsApi::allow_set_foreground_window(process_id)?;
WindowsApi::set_process_dpi_awareness_context()?;
let session_id = WindowsApi::process_id_to_session_id()?;
SESSION_ID.store(session_id, Ordering::SeqCst);
@@ -424,17 +456,14 @@ fn main() -> Result<()> {
#[cfg(feature = "deadlock_detection")]
detect_deadlocks();
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();
let winevent_listener = winevent_listener::new(Arc::new(Mutex::new(outgoing)));
winevent_listener.start();
Hidden::create("komorebi-hidden")?;
let wm = Arc::new(Mutex::new(WindowManager::new(Arc::new(Mutex::new(
incoming,
)))?));
@@ -446,6 +475,10 @@ fn main() -> Result<()> {
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
};
if let Some(port) = opts.tcp_port {
listen_for_commands_tcp(wm.clone(), port);
}
std::thread::spawn(|| {
load_configuration().expect("could not load configuration");
});
@@ -457,6 +490,8 @@ fn main() -> Result<()> {
}
}
wm.lock().retile_all(false)?;
listen_for_events(wm.clone());
if CUSTOM_FFM.load(Ordering::SeqCst) {

View File

@@ -21,9 +21,13 @@ pub struct Monitor {
#[getset(get_copy = "pub", set = "pub")]
id: isize,
#[getset(get = "pub", set = "pub")]
name: String,
#[getset(get = "pub", set = "pub")]
size: Rect,
#[getset(get = "pub", set = "pub")]
work_area_size: Rect,
#[getset(get_copy = "pub", set = "pub")]
work_area_offset: Option<Rect>,
workspaces: Ring<Workspace>,
#[serde(skip_serializing)]
#[getset(get_mut = "pub")]
@@ -32,14 +36,16 @@ pub struct Monitor {
impl_ring_elements!(Monitor, Workspace);
pub fn new(id: isize, size: Rect, work_area_size: Rect) -> Monitor {
pub fn new(id: isize, size: Rect, work_area_size: Rect, name: String) -> Monitor {
let mut workspaces = Ring::default();
workspaces.elements_mut().push_back(Workspace::default());
Monitor {
id,
name,
size,
work_area_size,
work_area_offset: None,
workspaces,
workspace_names: HashMap::default(),
}
@@ -121,6 +127,7 @@ impl Monitor {
let workspaces = self.workspaces_mut();
#[allow(clippy::option_if_let_else)]
let target_workspace = match workspaces.get_mut(target_workspace_idx) {
None => {
workspaces.resize(target_workspace_idx + 1, Workspace::default());
@@ -176,6 +183,11 @@ impl Monitor {
invisible_borders: &Rect,
) -> Result<()> {
let work_area = *self.work_area_size();
let offset = if self.work_area_offset().is_some() {
self.work_area_offset()
} else {
offset
};
self.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?

View File

@@ -2,15 +2,20 @@ use std::fs::File;
use std::fs::OpenOptions;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::io::Write;
use std::net::TcpListener;
use std::net::TcpStream;
use std::num::NonZeroUsize;
use std::str::FromStr;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time::Duration;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use miow::pipe::connect;
use net2::TcpStreamExt;
use parking_lot::Mutex;
use schemars::schema_for;
use uds_windows::UnixStream;
@@ -26,6 +31,7 @@ use komorebi_core::Sizing;
use komorebi_core::SocketMessage;
use komorebi_core::StateQuery;
use komorebi_core::WindowContainerBehaviour;
use komorebi_core::WindowKind;
use crate::border::Border;
use crate::current_virtual_desktop;
@@ -36,18 +42,28 @@ use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
use crate::BORDER_COLOUR_SINGLE;
use crate::BORDER_COLOUR_STACK;
use crate::BORDER_ENABLED;
use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_WIDTH;
use crate::CUSTOM_FFM;
use crate::DATA_DIR;
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::MONITOR_INDEX_PREFERENCES;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::SUBSCRIPTION_PIPES;
use crate::TCP_CONNECTIONS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
@@ -60,10 +76,10 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
.expect("could not clone unix listener");
std::thread::spawn(move || {
tracing::info!("listening");
tracing::info!("listening on komorebi.sock");
for client in listener.incoming() {
match client {
Ok(stream) => match wm.lock().read_commands(stream) {
Ok(stream) => match read_commands_uds(&wm, stream) {
Ok(()) => {}
Err(error) => tracing::error!("{}", error),
},
@@ -76,6 +92,48 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
});
}
#[tracing::instrument]
pub fn listen_for_commands_tcp(wm: Arc<Mutex<WindowManager>>, port: usize) {
let listener =
TcpListener::bind(format!("0.0.0.0:{}", port)).expect("could not start tcp server");
std::thread::spawn(move || {
tracing::info!("listening on 0.0.0.0:43663");
for client in listener.incoming() {
match client {
Ok(mut stream) => {
stream
.set_keepalive(Some(Duration::from_secs(30)))
.expect("TCP keepalive should be set");
let addr = stream
.peer_addr()
.expect("incoming connection should have an address")
.to_string();
let mut connections = TCP_CONNECTIONS.lock();
connections.insert(
addr.clone(),
stream.try_clone().expect("stream should be cloneable"),
);
tracing::info!("listening for incoming tcp messages from {}", &addr);
match read_commands_tcp(&wm, &mut stream, &addr) {
Ok(()) => {}
Err(error) => tracing::error!("{}", error),
}
}
Err(error) => {
tracing::error!("{}", error);
break;
}
}
}
});
}
impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn process_command(&mut self, message: SocketMessage) -> Result<()> {
@@ -100,6 +158,7 @@ impl WindowManager {
if self.focused_workspace()?.visible_windows().is_empty() {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.hide()?;
BORDER_HIDDEN.store(true, Ordering::SeqCst);
}
}
_ => {}
@@ -107,6 +166,7 @@ impl WindowManager {
match message {
SocketMessage::Promote => self.promote_container_to_front()?,
SocketMessage::PromoteFocus => self.promote_focus_to_front()?,
SocketMessage::FocusWindow(direction) => {
self.focus_container_in_direction(direction)?;
}
@@ -124,22 +184,56 @@ impl WindowManager {
SocketMessage::CycleStack(direction) => {
self.cycle_container_window_in_direction(direction)?;
}
SocketMessage::ForceFocus => {
let focused_window = self.focused_window()?;
let focused_window_rect = WindowsApi::window_rect(focused_window.hwnd())?;
WindowsApi::center_cursor_in_rect(&focused_window_rect)?;
WindowsApi::left_click();
}
SocketMessage::Close => self.focused_window()?.close()?,
SocketMessage::Minimize => self.focused_window()?.minimize(),
SocketMessage::ToggleFloat => self.toggle_float()?,
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
SocketMessage::ToggleMaximize => self.toggle_maximize()?,
SocketMessage::ContainerPadding(monitor_idx, workspace_idx, size) => {
self.set_container_padding(monitor_idx, workspace_idx, size)?;
}
SocketMessage::NamedWorkspaceContainerPadding(ref workspace, size) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_container_padding(monitor_idx, workspace_idx, size)?;
}
}
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::NamedWorkspacePadding(ref workspace, size) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
let mut workspace_rules = WORKSPACE_RULES.lock();
workspace_rules.insert(id.to_string(), (monitor_idx, workspace_idx));
self.set_workspace_padding(monitor_idx, workspace_idx, size)?;
}
}
SocketMessage::InitialWorkspaceRule(_, ref id, monitor_idx, workspace_idx) => {
self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?;
}
SocketMessage::InitialNamedWorkspaceRule(_, ref id, ref workspace) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.handle_initial_workspace_rules(id, monitor_idx, workspace_idx)?;
}
}
SocketMessage::WorkspaceRule(_, ref id, monitor_idx, workspace_idx) => {
self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?;
}
SocketMessage::NamedWorkspaceRule(_, ref id, ref workspace) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?;
}
self.enforce_workspace_rules()?;
}
SocketMessage::ManageRule(_, ref id) => {
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
@@ -209,18 +303,87 @@ impl WindowManager {
SocketMessage::MoveContainerToWorkspaceNumber(workspace_idx) => {
self.move_container_to_workspace(workspace_idx, true)?;
}
SocketMessage::CycleMoveContainerToWorkspace(direction) => {
let focused_monitor = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor"))?;
let focused_workspace_idx = focused_monitor.focused_workspace_idx();
let workspaces = focused_monitor.workspaces().len();
let workspace_idx = direction.next_idx(
focused_workspace_idx,
NonZeroUsize::new(workspaces)
.ok_or_else(|| anyhow!("there must be at least one workspace"))?,
);
self.move_container_to_workspace(workspace_idx, true)?;
}
SocketMessage::MoveContainerToMonitorNumber(monitor_idx) => {
self.move_container_to_monitor(monitor_idx, None, true)?;
}
SocketMessage::CycleMoveContainerToMonitor(direction) => {
let monitor_idx = direction.next_idx(
self.focused_monitor_idx(),
NonZeroUsize::new(self.monitors().len())
.ok_or_else(|| anyhow!("there must be at least one monitor"))?,
);
self.move_container_to_monitor(monitor_idx, None, true)?;
}
SocketMessage::SendContainerToWorkspaceNumber(workspace_idx) => {
self.move_container_to_workspace(workspace_idx, false)?;
}
SocketMessage::CycleSendContainerToWorkspace(direction) => {
let focused_monitor = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor"))?;
let focused_workspace_idx = focused_monitor.focused_workspace_idx();
let workspaces = focused_monitor.workspaces().len();
let workspace_idx = direction.next_idx(
focused_workspace_idx,
NonZeroUsize::new(workspaces)
.ok_or_else(|| anyhow!("there must be at least one workspace"))?,
);
self.move_container_to_workspace(workspace_idx, false)?;
}
SocketMessage::SendContainerToMonitorNumber(monitor_idx) => {
self.move_container_to_monitor(monitor_idx, None, false)?;
}
SocketMessage::CycleSendContainerToMonitor(direction) => {
let monitor_idx = direction.next_idx(
self.focused_monitor_idx(),
NonZeroUsize::new(self.monitors().len())
.ok_or_else(|| anyhow!("there must be at least one monitor"))?,
);
self.move_container_to_monitor(monitor_idx, None, false)?;
}
SocketMessage::SendContainerToMonitorWorkspaceNumber(monitor_idx, workspace_idx) => {
self.move_container_to_monitor(monitor_idx, Option::from(workspace_idx), false)?;
}
SocketMessage::SendContainerToNamedWorkspace(ref workspace) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.move_container_to_monitor(
monitor_idx,
Option::from(workspace_idx),
false,
)?;
}
}
SocketMessage::MoveContainerToNamedWorkspace(ref workspace) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.move_container_to_monitor(monitor_idx, Option::from(workspace_idx), true)?;
}
}
SocketMessage::MoveWorkspaceToMonitorNumber(monitor_idx) => {
self.move_workspace_to_monitor(monitor_idx)?;
}
@@ -295,15 +458,69 @@ impl WindowManager {
SocketMessage::ClearWorkspaceLayoutRules(monitor_idx, workspace_idx) => {
self.clear_workspace_layout_rules(monitor_idx, workspace_idx)?;
}
SocketMessage::NamedWorkspaceLayoutCustom(ref workspace, ref path) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_workspace_layout_custom(monitor_idx, workspace_idx, path.clone())?;
}
}
SocketMessage::NamedWorkspaceTiling(ref workspace, tile) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_workspace_tiling(monitor_idx, workspace_idx, tile)?;
}
}
SocketMessage::NamedWorkspaceLayout(ref workspace, layout) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.set_workspace_layout_default(monitor_idx, workspace_idx, layout)?;
}
}
SocketMessage::NamedWorkspaceLayoutRule(ref workspace, at_container_count, layout) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.add_workspace_layout_default_rule(
monitor_idx,
workspace_idx,
at_container_count,
layout,
)?;
}
}
SocketMessage::NamedWorkspaceLayoutCustomRule(
ref workspace,
at_container_count,
ref path,
) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.add_workspace_layout_custom_rule(
monitor_idx,
workspace_idx,
at_container_count,
path.clone(),
)?;
}
}
SocketMessage::ClearNamedWorkspaceLayoutRules(ref workspace) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(workspace)
{
self.clear_workspace_layout_rules(monitor_idx, workspace_idx)?;
}
}
SocketMessage::CycleFocusWorkspace(direction) => {
// This is to ensure that even on an empty workspace on a secondary monitor, the
// secondary monitor where the cursor is focused will be used as the target for
// the workspace switch op
let monitor_idx = self.monitor_idx_from_current_pos().ok_or_else(|| {
anyhow!("there is no monitor associated with the current cursor position")
})?;
self.focus_monitor(monitor_idx)?;
if let Some(monitor_idx) = self.monitor_idx_from_current_pos() {
self.focus_monitor(monitor_idx)?;
}
let focused_monitor = self
.focused_monitor()
@@ -319,22 +536,41 @@ impl WindowManager {
);
self.focus_workspace(workspace_idx)?;
if BORDER_ENABLED.load(Ordering::SeqCst) {
self.show_border()?;
};
}
SocketMessage::FocusWorkspaceNumber(workspace_idx) => {
// This is to ensure that even on an empty workspace on a secondary monitor, the
// secondary monitor where the cursor is focused will be used as the target for
// the workspace switch op
let monitor_idx = self.monitor_idx_from_current_pos().ok_or_else(|| {
anyhow!("there is no monitor associated with the current cursor position")
})?;
if let Some(monitor_idx) = self.monitor_idx_from_current_pos() {
self.focus_monitor(monitor_idx)?;
}
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
if BORDER_ENABLED.load(Ordering::SeqCst) {
self.show_border()?;
};
}
SocketMessage::FocusMonitorWorkspaceNumber(monitor_idx, workspace_idx) => {
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
}
SocketMessage::FocusNamedWorkspace(ref name) => {
if let Some((monitor_idx, workspace_idx)) =
self.monitor_workspace_index_by_name(name)
{
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
}
if BORDER_ENABLED.load(Ordering::SeqCst) {
self.show_border()?;
};
}
SocketMessage::Stop => {
tracing::info!(
"received stop command, restoring all hidden windows and terminating process"
@@ -347,9 +583,24 @@ impl WindowManager {
std::process::exit(0)
}
SocketMessage::MonitorIndexPreference(index_preference, left, top, right, bottom) => {
let mut monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
monitor_index_preferences.insert(
index_preference,
Rect {
left,
top,
right,
bottom,
},
);
}
SocketMessage::EnsureWorkspaces(monitor_idx, workspace_count) => {
self.ensure_workspaces_for_monitor(monitor_idx, workspace_count)?;
}
SocketMessage::EnsureNamedWorkspaces(monitor_idx, ref names) => {
self.ensure_named_workspaces_for_monitor(monitor_idx, names)?;
}
SocketMessage::NewWorkspace => {
self.new_workspace()?;
}
@@ -363,11 +614,11 @@ impl WindowManager {
Err(error) => error.to_string(),
};
let mut socket = HOME_DIR.clone();
let mut socket = DATA_DIR.clone();
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(&socket)?;
let mut stream = UnixStream::connect(socket)?;
stream.write_all(state.as_bytes())?;
}
SocketMessage::Query(query) => {
@@ -386,11 +637,11 @@ impl WindowManager {
}
.to_string();
let mut socket = HOME_DIR.clone();
let mut socket = DATA_DIR.clone();
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(&socket)?;
let mut stream = UnixStream::connect(socket)?;
stream.write_all(response.as_bytes())?;
}
SocketMessage::ResizeWindowEdge(direction, sizing) => {
@@ -641,6 +892,12 @@ impl WindowManager {
self.work_area_offset = Option::from(rect);
self.retile_all(false)?;
}
SocketMessage::MonitorWorkAreaOffset(monitor_idx, rect) => {
if let Some(monitor) = self.monitors_mut().get_mut(monitor_idx) {
monitor.set_work_area_offset(Option::from(rect));
self.retile_all(false)?;
}
}
SocketMessage::QuickSave => {
let workspace = self.focused_workspace()?;
let resize = workspace.resize_dimensions();
@@ -751,6 +1008,10 @@ impl WindowManager {
}
SocketMessage::ActiveWindowBorder(enable) => {
if enable {
if BORDER_HWND.load(Ordering::SeqCst) == 0 {
Border::create("komorebi-border-window")?;
}
BORDER_ENABLED.store(true, Ordering::SeqCst);
self.show_border()?;
} else {
@@ -758,22 +1019,100 @@ impl WindowManager {
self.hide_border()?;
}
}
SocketMessage::ActiveWindowBorderColour(r, g, b) => {
let hwnd = BORDER_HWND.load(Ordering::SeqCst);
WindowsApi::change_border_colour(hwnd, r, g, b)?;
SocketMessage::ActiveWindowBorderColour(kind, r, g, b) => {
match kind {
WindowKind::Single => {
BORDER_COLOUR_SINGLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
BORDER_COLOUR_CURRENT.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
WindowKind::Stack => {
BORDER_COLOUR_STACK.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
WindowKind::Monocle => {
BORDER_COLOUR_MONOCLE.store(r | (g << 8) | (b << 16), Ordering::SeqCst);
}
}
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::ActiveWindowBorderWidth(width) => {
BORDER_WIDTH.store(width, Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::ActiveWindowBorderOffset(offset) => {
let mut current_border_offset = BORDER_OFFSET.lock();
let new_border_offset = Rect {
left: offset,
top: offset,
right: offset * 2,
bottom: offset * 2,
};
*current_border_offset = Option::from(new_border_offset);
WindowsApi::invalidate_border_rect()?;
}
SocketMessage::AltFocusHack(enable) => {
ALT_FOCUS_HACK.store(enable, Ordering::SeqCst);
}
SocketMessage::NotificationSchema => {
let notification = schema_for!(Notification);
let schema = serde_json::to_string_pretty(&notification)?;
let mut socket = HOME_DIR.clone();
let mut socket = DATA_DIR.clone();
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(&socket)?;
let mut stream = UnixStream::connect(socket)?;
stream.write_all(schema.as_bytes())?;
}
SocketMessage::SocketSchema => {
let socket_message = schema_for!(SocketMessage);
let schema = serde_json::to_string_pretty(&socket_message)?;
let mut socket = DATA_DIR.clone();
socket.push("komorebic.sock");
let socket = socket.as_path();
let mut stream = UnixStream::connect(socket)?;
stream.write_all(schema.as_bytes())?;
}
};
match message {
SocketMessage::ToggleMonocle => {
let current = BORDER_COLOUR_CURRENT.load(Ordering::SeqCst);
let monocle = BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst);
if monocle != 0 {
if current == monocle {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
} else {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
}
}
}
SocketMessage::StackWindow(_) => {
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
if stack != 0 {
BORDER_COLOUR_CURRENT
.store(BORDER_COLOUR_STACK.load(Ordering::SeqCst), Ordering::SeqCst);
}
}
SocketMessage::UnstackWindow => {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
}
_ => {}
}
match message {
SocketMessage::ChangeLayout(_)
| SocketMessage::ChangeLayoutCustom(_)
@@ -788,7 +1127,19 @@ impl WindowManager {
| SocketMessage::ToggleMonocle
| SocketMessage::ToggleMaximize
| SocketMessage::Promote
| SocketMessage::PromoteFocus
| SocketMessage::StackWindow(_)
| SocketMessage::UnstackWindow
| SocketMessage::Retile
// Adding this one so that changes can be seen instantly after
// modifying the active window border offset
| SocketMessage::ActiveWindowBorderOffset(_)
// Adding this one because sometimes EVENT_SYSTEM_FOREGROUND isn't
// getting sent on FocusWindow, meaning the border won't be set
// when processing events
| SocketMessage::FocusWindow(_)
| SocketMessage::InvisibleBorders(_)
| SocketMessage::WorkAreaOffset(_)
| SocketMessage::MoveWindow(_) => {
let foreground = WindowsApi::foreground_window()?;
let foreground_window = Window { hwnd: foreground };
@@ -830,31 +1181,131 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self, stream))]
pub fn read_commands(&mut self, stream: UnixStream) -> Result<()> {
let stream = BufReader::new(stream);
for line in stream.lines() {
let message = SocketMessage::from_str(&line?)?;
#[tracing::instrument(skip(self))]
fn handle_initial_workspace_rules(
&mut self,
id: &String,
monitor_idx: usize,
workspace_idx: usize,
) -> Result<()> {
self.handle_workspace_rules(id, monitor_idx, workspace_idx, true)?;
if self.is_paused {
return match message {
SocketMessage::TogglePause | SocketMessage::State | SocketMessage::Stop => {
Ok(self.process_command(message)?)
}
_ => {
tracing::trace!("ignoring while paused");
Ok(())
}
};
}
Ok(())
}
self.process_command(message.clone())?;
notify_subscribers(&serde_json::to_string(&Notification {
event: NotificationEvent::Socket(message.clone()),
state: self.as_ref().into(),
})?)?;
#[tracing::instrument(skip(self))]
fn handle_definitive_workspace_rules(
&mut self,
id: &String,
monitor_idx: usize,
workspace_idx: usize,
) -> Result<()> {
self.handle_workspace_rules(id, monitor_idx, workspace_idx, false)?;
Ok(())
}
#[tracing::instrument(skip(self))]
fn handle_workspace_rules(
&mut self,
id: &String,
monitor_idx: usize,
workspace_idx: usize,
initial_workspace_rule: bool,
) -> Result<()> {
{
let mut workspace_rules = WORKSPACE_RULES.lock();
workspace_rules.insert(
id.to_string(),
(monitor_idx, workspace_idx, initial_workspace_rule),
);
}
self.enforce_workspace_rules()?;
Ok(())
}
}
pub fn read_commands_uds(wm: &Arc<Mutex<WindowManager>>, stream: UnixStream) -> Result<()> {
let stream = BufReader::new(stream);
for line in stream.lines() {
let message = SocketMessage::from_str(&line?)?;
let mut wm = wm.lock();
if wm.is_paused {
return match message {
SocketMessage::TogglePause | SocketMessage::State | SocketMessage::Stop => {
Ok(wm.process_command(message)?)
}
_ => {
tracing::trace!("ignoring while paused");
Ok(())
}
};
}
wm.process_command(message.clone())?;
notify_subscribers(&serde_json::to_string(&Notification {
event: NotificationEvent::Socket(message.clone()),
state: wm.as_ref().into(),
})?)?;
}
Ok(())
}
pub fn read_commands_tcp(
wm: &Arc<Mutex<WindowManager>>,
stream: &mut TcpStream,
addr: &str,
) -> Result<()> {
let mut stream = BufReader::new(stream);
loop {
let mut buf = vec![0; 1024];
match stream.read(&mut buf) {
Err(..) => {
tracing::warn!("removing disconnected tcp client: {addr}");
let mut connections = TCP_CONNECTIONS.lock();
connections.remove(addr);
break;
}
Ok(size) => {
let message = if let Ok(message) =
SocketMessage::from_str(&String::from_utf8_lossy(&buf[..size]))
{
message
} else {
tracing::warn!("client sent an invalid message, disconnecting: {addr}");
let mut connections = TCP_CONNECTIONS.lock();
connections.remove(addr);
break;
};
let mut wm = wm.lock();
if wm.is_paused {
return match message {
SocketMessage::TogglePause | SocketMessage::State | SocketMessage::Stop => {
Ok(wm.process_command(message)?)
}
_ => {
tracing::trace!("ignoring while paused");
Ok(())
}
};
}
wm.process_command(message.clone())?;
notify_subscribers(&serde_json::to_string(&Notification {
event: NotificationEvent::Socket(message.clone()),
state: wm.as_ref().into(),
})?)?;
}
}
}
Ok(())
}

View File

@@ -20,6 +20,10 @@ use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::Notification;
use crate::NotificationEvent;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_COLOUR_MONOCLE;
use crate::BORDER_COLOUR_SINGLE;
use crate::BORDER_COLOUR_STACK;
use crate::BORDER_ENABLED;
use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
@@ -71,9 +75,9 @@ impl WindowManager {
// Make sure we have the most recently focused monitor from any event
match event {
WindowManagerEvent::MonitorPoll(_, window)
| WindowManagerEvent::FocusChange(_, window)
WindowManagerEvent::FocusChange(_, window)
| WindowManagerEvent::Show(_, window)
| WindowManagerEvent::DisplayChange(window)
| WindowManagerEvent::MoveResizeEnd(_, window) => {
self.reconcile_monitors()?;
@@ -94,7 +98,9 @@ impl WindowManager {
//
// This check ensures that we only update the focused monitor when the window
// triggering monitor reconciliation is known to not be tied to a specific monitor.
if window.class()? != "OleMainThreadWndClass" {
if window.class()? != "OleMainThreadWndClass"
&& self.focused_monitor_idx() != monitor_idx
{
self.focus_monitor(monitor_idx)?;
}
}
@@ -106,6 +112,12 @@ impl WindowManager {
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
let work_area = *monitor.work_area_size();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
for (j, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
let reaped_orphans = workspace.reap_orphans()?;
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
@@ -132,12 +144,16 @@ impl WindowManager {
match event {
WindowManagerEvent::Raise(window) => {
window.raise()?;
window.raise();
self.has_pending_raise_op = false;
}
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?;
let mut already_moved_window_handles = self.already_moved_window_handles.lock();
already_moved_window_handles.remove(&window.hwnd);
}
WindowManagerEvent::Minimize(_, window) => {
let mut hide = false;
@@ -181,25 +197,33 @@ impl WindowManager {
self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?;
}
let mut already_moved_window_handles = self.already_moved_window_handles.lock();
already_moved_window_handles.remove(&window.hwnd);
}
WindowManagerEvent::FocusChange(_, window) => {
let workspace = self.focused_workspace_mut()?;
if workspace
if !workspace
.floating_windows()
.iter()
.any(|w| w.hwnd == window.hwnd)
{
return Ok(());
}
if let Some(w) = workspace.maximized_window() {
if w.hwnd == window.hwnd {
return Ok(());
}
}
if let Some(w) = workspace.maximized_window() {
if w.hwnd == window.hwnd {
return Ok(());
if let Some(monocle) = workspace.monocle_container() {
if let Some(window) = monocle.focused_window() {
window.focus(false)?;
}
} else {
self.focused_workspace_mut()?
.focus_container_by_window(window.hwnd)?;
}
}
self.focused_workspace_mut()?
.focus_container_by_window(window.hwnd)?;
}
WindowManagerEvent::Show(_, window) | WindowManagerEvent::Manage(window) => {
let mut switch_to = None;
@@ -265,7 +289,7 @@ impl WindowManager {
}
}
}
WindowManagerEvent::MoveResizeStart(_, _) => {
WindowManagerEvent::MoveResizeStart(_, window) => {
let monitor_idx = self.focused_monitor_idx();
let workspace_idx = self
.focused_monitor()
@@ -278,6 +302,8 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no workspace with this idx"))?
.focused_container_idx();
WindowsApi::bring_window_to_top(window.hwnd())?;
self.pending_move_op = Option::from((monitor_idx, workspace_idx, container_idx));
}
WindowManagerEvent::MoveResizeEnd(_, window) => {
@@ -295,147 +321,148 @@ impl WindowManager {
let invisible_borders = self.invisible_borders;
let workspace = self.focused_workspace_mut()?;
if workspace
if !workspace
.floating_windows()
.iter()
.any(|w| w.hwnd == window.hwnd)
{
return Ok(());
}
let focused_container_idx = workspace.focused_container_idx();
let focused_container_idx = workspace.focused_container_idx();
let mut new_position = WindowsApi::window_rect(window.hwnd())?;
let mut new_position = WindowsApi::window_rect(window.hwnd())?;
let old_position = *workspace
.latest_layout()
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
// have any latest layout set. We fall back to a Default for Rect
// which allows us to make a reasonable guess that the drag has taken
// place across a monitor boundary to an empty workspace
.unwrap_or(&Rect::default());
let old_position = *workspace
.latest_layout()
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
// have any latest layout set. We fall back to a Default for Rect
// which allows us to make a reasonable guess that the drag has taken
// place across a monitor boundary to an empty workspace
.unwrap_or(&Rect::default());
// This will be true if we have moved to an empty workspace on another monitor
let mut moved_across_monitors = old_position == Rect::default();
// This will be true if we have moved to an empty workspace on another monitor
let mut moved_across_monitors = old_position == Rect::default();
if let Some((origin_monitor_idx, _, _)) = pending {
// If we didn't move to another monitor with an empty workspace, it is
// still possible that we moved to another monitor with a populated workspace
if !moved_across_monitors {
// So we'll check if the origin monitor index and the target monitor index
// are different, if they are, we can set the override
moved_across_monitors = origin_monitor_idx != target_monitor_idx;
}
}
// Adjust for the invisible borders
new_position.left += invisible_borders.left;
new_position.top += invisible_borders.top;
new_position.right -= invisible_borders.right;
new_position.bottom -= invisible_borders.bottom;
let resize = Rect {
left: new_position.left - old_position.left,
top: new_position.top - old_position.top,
right: new_position.right - old_position.right,
bottom: new_position.bottom - old_position.bottom,
};
// If we have moved across the monitors, use that override, otherwise determine
// if a move has taken place by ruling out a resize
let is_move = moved_across_monitors
|| resize.right == 0 && resize.bottom == 0
|| resize.right.abs() == invisible_borders.right
&& resize.bottom.abs() == invisible_borders.bottom;
if is_move {
tracing::info!("moving with mouse");
if moved_across_monitors {
if let Some((
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
)) = pending
{
let target_workspace_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace_idx();
let target_container_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace()
.ok_or_else(|| {
anyhow!("there is no focused workspace for this monitor")
})?
.container_idx_from_current_point()
// Default to 0 in the case of an empty workspace
.unwrap_or(0);
self.transfer_container(
(
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
),
(
target_monitor_idx,
target_workspace_idx,
target_container_idx,
),
)?;
// We want to make sure both the origin and target monitors are updated,
// so that we don't have ghost tiles until we force an interaction on
// the origin monitor's focused workspace
self.focus_monitor(origin_monitor_idx)?;
self.focus_workspace(origin_workspace_idx)?;
self.update_focused_workspace(false)?;
self.focus_monitor(target_monitor_idx)?;
self.focus_workspace(target_workspace_idx)?;
self.update_focused_workspace(false)?;
if let Some((origin_monitor_idx, _, _)) = pending {
// If we didn't move to another monitor with an empty workspace, it is
// still possible that we moved to another monitor with a populated workspace
if !moved_across_monitors {
// So we'll check if the origin monitor index and the target monitor index
// are different, if they are, we can set the override
moved_across_monitors = origin_monitor_idx != target_monitor_idx;
}
}
// Adjust for the invisible borders
new_position.left += invisible_borders.left;
new_position.top += invisible_borders.top;
new_position.right -= invisible_borders.right;
new_position.bottom -= invisible_borders.bottom;
let resize = Rect {
left: new_position.left - old_position.left,
top: new_position.top - old_position.top,
right: new_position.right - old_position.right,
bottom: new_position.bottom - old_position.bottom,
};
// If we have moved across the monitors, use that override, otherwise determine
// if a move has taken place by ruling out a resize
let is_move = moved_across_monitors
|| resize.right == 0 && resize.bottom == 0
|| resize.right.abs() == invisible_borders.right
&& resize.bottom.abs() == invisible_borders.bottom;
if is_move {
tracing::info!("moving with mouse");
if moved_across_monitors {
if let Some((
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
)) = pending
{
let target_workspace_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace_idx();
let target_container_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace()
.ok_or_else(|| {
anyhow!("there is no focused workspace for this monitor")
})?
.container_idx_from_current_point()
// Default to 0 in the case of an empty workspace
.unwrap_or(0);
self.transfer_container(
(
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
),
(
target_monitor_idx,
target_workspace_idx,
target_container_idx,
),
)?;
// We want to make sure both the origin and target monitors are updated,
// so that we don't have ghost tiles until we force an interaction on
// the origin monitor's focused workspace
self.focus_monitor(origin_monitor_idx)?;
self.focus_workspace(origin_workspace_idx)?;
self.update_focused_workspace(false)?;
self.focus_monitor(target_monitor_idx)?;
self.focus_workspace(target_workspace_idx)?;
self.update_focused_workspace(false)?;
}
// Here we handle a simple move on the same monitor which is treated as
// a container swap
} else {
match new_window_behaviour {
WindowContainerBehaviour::Create => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace
.swap_containers(focused_container_idx, target_idx);
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(
self.mouse_follows_focus,
)?;
}
}
}
WindowContainerBehaviour::Append => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace.move_window_to_container(target_idx)?;
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(
self.mouse_follows_focus,
)?;
}
}
}
}
}
// Here we handle a simple move on the same monitor which is treated as
// a container swap
} else {
match new_window_behaviour {
WindowContainerBehaviour::Create => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace
.swap_containers(focused_container_idx, target_idx);
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(self.mouse_follows_focus)?;
}
}
}
WindowContainerBehaviour::Append => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace.move_window_to_container(target_idx)?;
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(self.mouse_follows_focus)?;
}
}
}
}
}
} else {
tracing::info!("resizing with mouse");
let mut ops = vec![];
tracing::info!("resizing with mouse");
let mut ops = vec![];
macro_rules! resize_op {
macro_rules! resize_op {
($coordinate:expr, $comparator:tt, $direction:expr) => {{
let adjusted = $coordinate * 2;
let sizing = if adjusted $comparator 0 {
@@ -448,30 +475,34 @@ impl WindowManager {
}};
}
if resize.left != 0 {
ops.push(resize_op!(resize.left, >, OperationDirection::Left));
}
if resize.left != 0 {
ops.push(resize_op!(resize.left, >, OperationDirection::Left));
}
if resize.top != 0 {
ops.push(resize_op!(resize.top, >, OperationDirection::Up));
}
if resize.top != 0 {
ops.push(resize_op!(resize.top, >, OperationDirection::Up));
}
if resize.right != 0 && resize.left == 0 {
ops.push(resize_op!(resize.right, <, OperationDirection::Right));
}
if resize.right != 0 && resize.left == 0 {
ops.push(resize_op!(resize.right, <, OperationDirection::Right));
}
if resize.bottom != 0 && resize.top == 0 {
ops.push(resize_op!(resize.bottom, <, OperationDirection::Down));
}
if resize.bottom != 0 && resize.top == 0 {
ops.push(resize_op!(resize.bottom, <, OperationDirection::Down));
}
for (edge, sizing, delta) in ops {
self.resize_window(edge, sizing, delta, true)?;
}
for (edge, sizing, delta) in ops {
self.resize_window(edge, sizing, delta, true)?;
}
self.update_focused_workspace(false)?;
self.update_focused_workspace(false)?;
}
}
}
WindowManagerEvent::MonitorPoll(..) | WindowManagerEvent::MouseCapture(..) => {}
WindowManagerEvent::DisplayChange(..)
| WindowManagerEvent::MouseCapture(..)
| WindowManagerEvent::Cloak(..)
| WindowManagerEvent::Uncloak(..) => {}
};
if *self.focused_workspace()?.tile() && BORDER_ENABLED.load(Ordering::SeqCst) {
@@ -481,21 +512,79 @@ impl WindowManager {
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);
WindowManagerEvent::MoveResizeEnd(_, window)
| WindowManagerEvent::Show(_, window)
| WindowManagerEvent::FocusChange(_, window)
| WindowManagerEvent::Hide(_, window)
| WindowManagerEvent::Minimize(_, window) => {
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(*window, &self.invisible_borders, activate)?;
let mut target_window = None;
let mut target_window_is_monocle = false;
if self
.focused_workspace()?
.floating_windows()
.iter()
.any(|w| w.hwnd == window.hwnd)
{
target_window = Option::from(*window);
WindowsApi::raise_window(border.hwnd())?;
};
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
if let Some(monocle_container) = self.focused_workspace()?.monocle_container() {
if let Some(window) = monocle_container.focused_window() {
target_window = Option::from(*window);
target_window_is_monocle = true;
}
}
if target_window.is_none() {
match self.focused_container() {
// if there is no focused container, the desktop is empty
Err(..) => {
WindowsApi::hide_border_window(border.hwnd())?;
}
Ok(container) => {
if !(matches!(event, WindowManagerEvent::Minimize(_, _))
&& container.windows().len() == 1)
{
let container_size = self.focused_container()?.windows().len();
target_window = Option::from(*self.focused_window()?);
if target_window_is_monocle {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
} else if container_size > 1 {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_STACK.load(Ordering::SeqCst),
Ordering::SeqCst,
);
} else {
BORDER_COLOUR_CURRENT.store(
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst),
Ordering::SeqCst,
);
}
}
}
}
}
if let Some(target_window) = target_window {
let window = target_window;
let mut rect = WindowsApi::window_rect(window.hwnd())?;
rect.top -= self.invisible_borders.bottom;
rect.bottom += self.invisible_borders.bottom;
let activate = BORDER_HIDDEN.load(Ordering::SeqCst);
WindowsApi::invalidate_border_rect()?;
border.set_position(target_window, &self.invisible_borders, activate)?;
if activate {
BORDER_HIDDEN.store(false, Ordering::SeqCst);
}
}
}
_ => {}

View File

@@ -1,7 +1,9 @@
use crate::com::SetCloak;
use std::convert::TryFrom;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Write as _;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -11,6 +13,9 @@ use serde::ser::SerializeStruct;
use serde::Serialize;
use serde::Serializer;
use windows::Win32::Foundation::HWND;
use winput::press;
use winput::release;
use winput::Vk;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
@@ -20,6 +25,7 @@ use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::ALT_FOCUS_HACK;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDDEN_HWNDS;
@@ -148,6 +154,7 @@ impl Window {
match *hiding_behaviour {
HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd()),
HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd()),
HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 2),
}
}
@@ -160,7 +167,21 @@ impl Window {
programmatically_hidden_hwnds.remove(idx);
}
WindowsApi::restore_window(self.hwnd());
let hiding_behaviour = HIDING_BEHAVIOUR.lock();
match *hiding_behaviour {
HidingBehaviour::Hide | HidingBehaviour::Minimize => {
WindowsApi::restore_window(self.hwnd());
}
HidingBehaviour::Cloak => SetCloak(self.hwnd(), 1, 0),
}
}
pub fn minimize(self) {
WindowsApi::minimize_window(self.hwnd());
}
pub fn close(self) -> Result<()> {
WindowsApi::close_window(self.hwnd())
}
pub fn maximize(self) {
@@ -187,25 +208,55 @@ impl Window {
WindowsApi::unmaximize_window(self.hwnd());
}
pub fn raise(self) -> Result<()> {
pub fn raise(self) {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
let current_thread_id = WindowsApi::current_thread_id();
WindowsApi::attach_thread_input(current_thread_id, window_thread_id, true)?;
// This can be allowed to fail if a window doesn't have a message queue or if a journal record
// hook has been installed
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput#remarks
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, true) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not attach to window thread input processing mechanism, but continuing execution of raise(): {}",
error
);
}
};
// Raise Window to foreground
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => {}
Err(error) => {
tracing::error!(
"could not set as foreground window, but continuing execution of focus(): {}",
"could not set as foreground window, but continuing execution of raise(): {}",
error
);
}
};
// This isn't really needed when the above command works as expected via AHK
WindowsApi::set_focus(self.hwnd())
match WindowsApi::set_focus(self.hwnd()) {
Ok(_) => {}
Err(error) => {
tracing::error!(
"could not set focus, but continuing execution of raise(): {}",
error
);
}
};
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, false) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not detach from window thread input processing mechanism, but continuing execution of raise(): {}",
error
);
}
};
}
pub fn focus(self, mouse_follows_focus: bool) -> Result<()> {
@@ -227,15 +278,41 @@ impl Window {
};
// Raise Window to foreground
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => {}
Err(error) => {
tracing::error!(
"could not set as foreground window, but continuing execution of focus(): {}",
error
);
let mut foregrounded = false;
let mut tried_resetting_foreground_access = false;
let mut max_attempts = 10;
let hotkey_uses_alt = WindowsApi::alt_is_pressed();
while !foregrounded && max_attempts > 0 {
if ALT_FOCUS_HACK.load(Ordering::SeqCst) {
press(Vk::Alt);
}
};
match WindowsApi::set_foreground_window(self.hwnd()) {
Ok(_) => {
foregrounded = true;
}
Err(error) => {
max_attempts -= 1;
tracing::error!(
"could not set as foreground window, but continuing execution of focus(): {}",
error
);
// If this still doesn't work then maybe try https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-locksetforegroundwindow
if !tried_resetting_foreground_access {
let process_id = WindowsApi::current_process_id();
if WindowsApi::allow_set_foreground_window(process_id).is_ok() {
tried_resetting_foreground_access = true;
}
}
}
};
if ALT_FOCUS_HACK.load(Ordering::SeqCst) && !hotkey_uses_alt {
release(Vk::Alt);
}
}
// Center cursor in Window
if mouse_follows_focus {
@@ -253,14 +330,42 @@ impl Window {
}
};
match WindowsApi::attach_thread_input(current_thread_id, window_thread_id, false) {
Ok(()) => {}
Err(error) => {
tracing::error!(
"could not detach from window thread input processing mechanism, but continuing execution of focus(): {}",
error
);
}
};
Ok(())
}
pub fn transparent(self) -> Result<()> {
let mut ex_style = self.ex_style()?;
ex_style.insert(ExtendedWindowStyle::LAYERED);
self.update_ex_style(ex_style)?;
WindowsApi::set_transparent(self.hwnd());
Ok(())
}
pub fn opaque(self) -> Result<()> {
let mut ex_style = self.ex_style()?;
ex_style.remove(ExtendedWindowStyle::LAYERED);
self.update_ex_style(ex_style)
}
#[allow(dead_code)]
pub fn update_style(self, style: WindowStyle) -> Result<()> {
WindowsApi::update_style(self.hwnd(), isize::try_from(style.bits())?)
}
pub fn update_ex_style(self, style: ExtendedWindowStyle) -> Result<()> {
WindowsApi::update_ex_style(self.hwnd(), isize::try_from(style.bits())?)
}
pub fn style(self) -> Result<WindowStyle> {
let bits = u32::try_from(WindowsApi::gwl_style(self.hwnd())?)?;
WindowStyle::from_bits(bits).ok_or_else(|| anyhow!("there is no gwl style"))
@@ -294,7 +399,7 @@ impl Window {
#[tracing::instrument(fields(exe, title))]
pub fn should_manage(self, event: Option<WindowManagerEvent>) -> Result<bool> {
if let Some(WindowManagerEvent::MonitorPoll(_, _)) = event {
if let Some(WindowManagerEvent::DisplayChange(_)) = event {
return Ok(true);
}
@@ -306,8 +411,14 @@ impl Window {
let is_cloaked = self.is_cloaked()?;
let mut allow_cloaked = false;
if let Some(WindowManagerEvent::Hide(_, _)) = event {
allow_cloaked = true;
if let Some(event) = event {
if matches!(
event,
WindowManagerEvent::Hide(_, _) | WindowManagerEvent::Cloak(_, _)
) {
allow_cloaked = true;
}
}
match (allow_cloaked, is_cloaked) {
@@ -384,6 +495,9 @@ fn window_is_eligible(
|| layered_whitelist.contains(title)
};
// TODO: might need this for transparency
// let allow_layered = true;
let allow_wsl2_gui = {
let wsl2_ui_processes = WSL2_UI_PROCESSES.lock();
wsl2_ui_processes.contains(exe_name)

View File

@@ -1,3 +1,5 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::VecDeque;
use std::io::ErrorKind;
use std::num::NonZeroUsize;
@@ -15,7 +17,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;
@@ -30,6 +31,7 @@ use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::WindowContainerBehaviour;
use crate::border::Border;
use crate::container::Container;
use crate::current_virtual_desktop;
use crate::load_configuration;
@@ -54,6 +56,7 @@ use crate::WORKSPACE_RULES;
#[derive(Debug)]
pub struct WindowManager {
pub monitors: Ring<Monitor>,
pub monitor_cache: HashMap<usize, Monitor>,
pub incoming_events: Arc<Mutex<Receiver<WindowManagerEvent>>>,
pub command_listener: UnixListener,
pub is_paused: bool,
@@ -69,6 +72,7 @@ pub struct WindowManager {
pub virtual_desktop_id: Option<Vec<u8>>,
pub has_pending_raise_op: bool,
pub pending_move_op: Option<(usize, usize, usize)>,
pub already_moved_window_handles: Arc<Mutex<HashSet<isize>>>,
}
#[derive(Debug, Serialize, JsonSchema)]
@@ -166,6 +170,7 @@ impl WindowManager {
Ok(Self {
monitors: Ring::default(),
monitor_cache: HashMap::new(),
incoming_events: incoming,
command_listener: listener,
is_paused: false,
@@ -186,6 +191,7 @@ impl WindowManager {
hotwatch: Hotwatch::new()?,
has_pending_raise_op: false,
pending_move_op: None,
already_moved_window_handles: Arc::new(Mutex::new(HashSet::new())),
})
}
@@ -205,7 +211,8 @@ impl WindowManager {
rect.bottom += self.invisible_borders.bottom;
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
border.set_position(foreground_window, &self.invisible_borders, true)
border.set_position(foreground_window, &self.invisible_borders, true)?;
WindowsApi::invalidate_border_rect()
}
#[tracing::instrument(skip(self))]
@@ -226,16 +233,16 @@ impl WindowManager {
pub fn watch_configuration(&mut self, enable: bool) -> Result<()> {
let home = HOME_DIR.clone();
let mut config_v1 = home.clone();
config_v1.push("komorebi.ahk");
let mut config_pwsh = home.clone();
config_pwsh.push("komorebi.ps1");
let mut config_v2 = home;
config_v2.push("komorebi.ahk2");
let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
if config_v1.exists() {
self.configure_watcher(enable, config_v1)?;
} else if config_v2.exists() {
self.configure_watcher(enable, config_v2)?;
if config_pwsh.exists() {
self.configure_watcher(enable, config_pwsh)?;
} else if config_ahk.exists() {
self.configure_watcher(enable, config_ahk)?;
}
Ok(())
@@ -325,44 +332,79 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn reconcile_monitors(&mut self) -> Result<()> {
if self.pending_move_op.is_some() {
return Ok(());
}
let valid_hmonitors = WindowsApi::valid_hmonitors()?;
let mut invalid = vec![];
let mut valid_names = vec![];
let before_count = self.monitors().len();
for monitor in self.monitors_mut() {
if !valid_hmonitors.contains(&monitor.id()) {
let mut mark_as_invalid = true;
// If an invalid hmonitor has at least one window in the window manager state,
// we can attempt to update its hmonitor id in-place so that it doesn't get reaped
if let Some(workspace) = monitor.focused_workspace() {
if let Some(container) = workspace.focused_container() {
if let Some(window) = container.focused_window() {
let actual_hmonitor = WindowsApi::monitor_from_window(window.hwnd());
if actual_hmonitor != monitor.id() {
monitor.set_id(actual_hmonitor);
mark_as_invalid = false;
}
}
}
}
if mark_as_invalid {
invalid.push(monitor.id());
for (valid_name, valid_id) in &valid_hmonitors {
let actual_name = monitor.name().clone();
if actual_name == *valid_name {
monitor.set_id(*valid_id);
valid_names.push(actual_name);
}
}
}
let mut orphaned_containers = vec![];
let mut invalid_indices = vec![];
for (i, invalid) in self
.monitors()
.iter()
.enumerate()
.filter(|(_, m)| !valid_names.contains(m.name()))
{
invalid_indices.push(i);
for workspace in invalid.workspaces() {
for container in workspace.containers() {
// Save the orphaned containers from an invalid monitor
// (which has most likely been disconnected)
orphaned_containers.push(container.clone());
}
}
}
for i in invalid_indices {
if let Some(monitor) = self.monitors().get(i) {
self.monitor_cache.insert(i, monitor.clone());
}
}
// Remove any invalid monitors from our state
self.monitors_mut().retain(|m| !invalid.contains(&m.id()));
self.monitors_mut()
.retain(|m| valid_names.contains(m.name()));
let after_count = self.monitors().len();
if let Some(primary) = self.monitors_mut().front_mut() {
if let Some(focused_ws) = primary.focused_workspace_mut() {
let focused_container_idx = focused_ws.focused_container_idx();
// Put the orphaned containers somewhere visible
for container in orphaned_containers {
focused_ws.add_container(container);
}
// Gotta reset the focus or the movement will feel "off"
if before_count != after_count {
focused_ws.focus_container(focused_container_idx);
}
}
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
for monitor in self.monitors_mut() {
let mut should_update = false;
let reference = WindowsApi::monitor(monitor.id())?;
// TODO: If this is different, force a redraw
// If we have lost a monitor, update everything to filter out any jank
let mut should_update = before_count != after_count;
let reference = WindowsApi::monitor(monitor.id())?;
if reference.work_area_size() != monitor.work_area_size() {
monitor.set_work_area_size(Rect {
left: reference.work_area_size().left,
@@ -390,12 +432,82 @@ impl WindowManager {
}
}
#[allow(clippy::needless_collect)]
let old_sizes = self
.monitors()
.iter()
.map(Monitor::size)
.copied()
.collect::<Vec<_>>();
// Check for and add any new monitors that may have been plugged in
WindowsApi::load_monitor_information(&mut self.monitors)?;
let mut check_cache = vec![];
for (i, m) in self.monitors().iter().enumerate() {
if !old_sizes.contains(m.size()) {
check_cache.push(i);
}
}
for i in check_cache {
if let Some(cached) = self.monitor_cache.get(&i).cloned() {
if let Some(monitor) = self.monitors_mut().get_mut(i) {
for (w_idx, workspace) in monitor.workspaces_mut().iter_mut().enumerate() {
if let Some(cached_workspace) = cached.workspaces().get(w_idx) {
workspace.set_layout(cached_workspace.layout().clone());
workspace.set_layout_rules(cached_workspace.layout_rules().clone());
workspace.set_layout_flip(cached_workspace.layout_flip());
workspace.set_workspace_padding(cached_workspace.workspace_padding());
workspace.set_container_padding(cached_workspace.container_padding());
}
}
}
}
}
let final_count = self.monitors().len();
if after_count != final_count {
self.retile_all(true)?;
// Second retile to fix DPI/resolution related jank when a window
// moves between monitors with different resolutions - this doesn't
// really get seen by the user since the screens are flickering anyway
// as a result of the display connections / disconnections
self.retile_all(true)?;
}
Ok(())
}
#[tracing::instrument(skip(self))]
fn add_window_handle_to_move_based_on_workspace_rule(
&self,
window_title: &String,
hwnd: isize,
origin_monitor_idx: usize,
origin_workspace_idx: usize,
target_monitor_idx: usize,
target_workspace_idx: usize,
to_move: &mut Vec<EnforceWorkspaceRuleOp>,
) -> () {
tracing::info!(
"{} should be on monitor {}, workspace {}",
window_title,
target_monitor_idx,
target_workspace_idx
);
// Create an operation outline and save it for later in the fn
to_move.push(EnforceWorkspaceRuleOp {
hwnd,
origin_monitor_idx,
origin_workspace_idx,
target_monitor_idx,
target_workspace_idx,
});
}
#[tracing::instrument(skip(self))]
pub fn enforce_workspace_rules(&mut self) -> Result<()> {
let mut to_move = vec![];
@@ -413,41 +525,43 @@ impl WindowManager {
for (j, workspace) in monitor.workspaces().iter().enumerate() {
// And all the visible windows (at the top of a container)
for window in workspace.visible_windows().into_iter().flatten() {
let mut already_moved_window_handles = self.already_moved_window_handles.lock();
let mut found_workspace_rule = workspace_rules.get(&window.exe()?);
if found_workspace_rule.is_none() {
found_workspace_rule = workspace_rules.get(&window.title()?);
}
// If the executable names or titles of any of those windows are in our rules map
if let Some((monitor_idx, workspace_idx)) = workspace_rules.get(&window.exe()?)
if let Some((monitor_idx, workspace_idx, apply_on_first_show_only)) =
found_workspace_rule
{
tracing::info!(
"{} should be on monitor {}, workspace {}",
window.title()?,
*monitor_idx,
*workspace_idx
);
if *apply_on_first_show_only {
if !already_moved_window_handles.contains(&window.hwnd) {
already_moved_window_handles.insert(window.hwnd);
// Create an operation outline and save it for later in the fn
to_move.push(EnforceWorkspaceRuleOp {
hwnd: window.hwnd,
origin_monitor_idx: i,
origin_workspace_idx: j,
target_monitor_idx: *monitor_idx,
target_workspace_idx: *workspace_idx,
});
} else if let Some((monitor_idx, workspace_idx)) =
workspace_rules.get(&window.title()?)
{
tracing::info!(
"{} should be on monitor {}, workspace {}",
window.title()?,
*monitor_idx,
*workspace_idx
);
to_move.push(EnforceWorkspaceRuleOp {
hwnd: window.hwnd,
origin_monitor_idx: i,
origin_workspace_idx: j,
target_monitor_idx: *monitor_idx,
target_workspace_idx: *workspace_idx,
});
self.add_window_handle_to_move_based_on_workspace_rule(
&window.title()?,
window.hwnd,
i,
j,
*monitor_idx,
*workspace_idx,
&mut to_move,
);
}
} else {
self.add_window_handle_to_move_based_on_workspace_rule(
&window.title()?,
window.hwnd,
i,
j,
*monitor_idx,
*workspace_idx,
&mut to_move,
);
}
}
}
}
@@ -520,6 +634,12 @@ impl WindowManager {
for monitor in self.monitors_mut() {
let work_area = *monitor.work_area_size();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?;
@@ -724,6 +844,9 @@ impl WindowManager {
hwnd: WindowsApi::desktop_window()?,
};
let rect = self.focused_monitor_size()?;
WindowsApi::center_cursor_in_rect(&rect)?;
// Calling this directly instead of the window.focus() wrapper because trying to
// attach to the thread of the desktop window always seems to result in "Access is
// denied (os error 5)"
@@ -962,12 +1085,6 @@ impl WindowManager {
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 new_idx = workspace.new_idx_for_direction(direction);
@@ -997,6 +1114,9 @@ impl WindowManager {
self.handle_unmanaged_window_behaviour()?;
let workspace = self.focused_workspace()?;
// removing this messes up the monitor / container / window index somewhere
// and results in the wrong window getting moved across the monitor boundary
if workspace.is_focused_window_monocle_or_maximized()? {
return Err(anyhow!(
"ignoring command while active window is in monocle mode or maximized"
@@ -1046,8 +1166,10 @@ impl WindowManager {
let origin_workspace =
self.focused_workspace_for_monitor_idx_mut(origin_monitor_idx)?;
origin_workspace
.focus_container(origin_workspace.focused_container_idx() - 1);
if origin_workspace.focused_container_idx() != 0 {
origin_workspace
.focus_container(origin_workspace.focused_container_idx() - 1);
}
}
}
@@ -1250,6 +1372,23 @@ impl WindowManager {
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn promote_focus_to_front(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
tracing::info!("promoting focus");
let workspace = self.focused_workspace_mut()?;
let target_idx = match workspace.layout() {
Layout::Default(_) => 0,
Layout::Custom(custom) => custom
.first_container_idx(custom.primary_idx().map_or(0, |primary_idx| primary_idx)),
};
workspace.focus_container(target_idx);
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn remove_window_from_container(&mut self) -> Result<()> {
self.handle_unmanaged_window_behaviour()?;
@@ -1552,6 +1691,11 @@ impl WindowManager {
let work_area = *monitor.work_area_size();
let focused_workspace_idx = monitor.focused_workspace_idx();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.workspaces_mut()
@@ -1593,6 +1737,11 @@ impl WindowManager {
let work_area = *monitor.work_area_size();
let focused_workspace_idx = monitor.focused_workspace_idx();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.workspaces_mut()
@@ -1634,6 +1783,11 @@ impl WindowManager {
let work_area = *monitor.work_area_size();
let focused_workspace_idx = monitor.focused_workspace_idx();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.workspaces_mut()
@@ -1672,6 +1826,11 @@ impl WindowManager {
let work_area = *monitor.work_area_size();
let focused_workspace_idx = monitor.focused_workspace_idx();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.workspaces_mut()
@@ -1709,6 +1868,11 @@ impl WindowManager {
let work_area = *monitor.work_area_size();
let focused_workspace_idx = monitor.focused_workspace_idx();
let offset = if monitor.work_area_offset().is_some() {
monitor.work_area_offset()
} else {
offset
};
let workspace = monitor
.workspaces_mut()
@@ -1745,6 +1909,30 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn ensure_named_workspaces_for_monitor(
&mut self,
monitor_idx: usize,
names: &Vec<String>,
) -> Result<()> {
tracing::info!("ensuring workspace count");
let monitor = self
.monitors_mut()
.get_mut(monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor"))?;
monitor.ensure_workspace_count(names.len());
for (workspace_idx, name) in names.iter().enumerate() {
if let Some(workspace) = monitor.workspaces_mut().get_mut(workspace_idx) {
workspace.set_name(Option::from(name.clone()));
}
}
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn set_workspace_padding(
&mut self,
@@ -1914,6 +2102,23 @@ impl WindowManager {
self.update_focused_workspace(false)
}
#[tracing::instrument(skip(self))]
pub fn monitor_workspace_index_by_name(&mut self, name: &str) -> Option<(usize, usize)> {
tracing::info!("looking up workspace by name");
for (monitor_idx, monitor) in self.monitors().iter().enumerate() {
for (workspace_idx, workspace) in monitor.workspaces().iter().enumerate() {
if let Some(workspace_name) = workspace.name() {
if workspace_name == name {
return Option::from((monitor_idx, workspace_idx));
}
}
}
}
None
}
#[tracing::instrument(skip(self))]
pub fn new_workspace(&mut self) -> Result<()> {
tracing::info!("adding new workspace");

View File

@@ -14,15 +14,17 @@ pub enum WindowManagerEvent {
Destroy(WinEvent, Window),
FocusChange(WinEvent, Window),
Hide(WinEvent, Window),
Cloak(WinEvent, Window),
Minimize(WinEvent, Window),
Show(WinEvent, Window),
Uncloak(WinEvent, Window),
MoveResizeStart(WinEvent, Window),
MoveResizeEnd(WinEvent, Window),
MouseCapture(WinEvent, Window),
Manage(Window),
Unmanage(Window),
Raise(Window),
MonitorPoll(WinEvent, Window),
DisplayChange(Window),
}
impl Display for WindowManagerEvent {
@@ -47,12 +49,18 @@ impl Display for WindowManagerEvent {
Self::Hide(winevent, window) => {
write!(f, "Hide (WinEvent: {}, Window: {})", winevent, window)
}
Self::Cloak(winevent, window) => {
write!(f, "Cloak (WinEvent: {}, Window: {})", winevent, window)
}
Self::Minimize(winevent, window) => {
write!(f, "Minimize (WinEvent: {}, Window: {})", winevent, window)
}
Self::Show(winevent, window) => {
write!(f, "Show (WinEvent: {}, Window: {})", winevent, window)
}
Self::Uncloak(winevent, window) => {
write!(f, "Uncloak (WinEvent: {}, Window: {})", winevent, window)
}
Self::MoveResizeStart(winevent, window) => {
write!(
f,
@@ -77,12 +85,8 @@ impl Display for WindowManagerEvent {
Self::Raise(window) => {
write!(f, "Raise (Window: {})", window)
}
Self::MonitorPoll(winevent, window) => {
write!(
f,
"MonitorPoll (WinEvent: {}, Window: {})",
winevent, window
)
Self::DisplayChange(window) => {
write!(f, "DisplayChange (Window: {})", window)
}
}
}
@@ -94,14 +98,16 @@ impl WindowManagerEvent {
Self::Destroy(_, window)
| Self::FocusChange(_, window)
| Self::Hide(_, window)
| Self::Cloak(_, window)
| Self::Minimize(_, window)
| Self::Show(_, window)
| Self::Uncloak(_, window)
| Self::MoveResizeStart(_, window)
| Self::MoveResizeEnd(_, window)
| Self::MouseCapture(_, window)
| Self::MonitorPoll(_, window)
| Self::Raise(window)
| Self::Manage(window)
| Self::DisplayChange(window)
| Self::Unmanage(window) => window,
}
}
@@ -110,16 +116,17 @@ impl WindowManagerEvent {
match winevent {
WinEvent::ObjectDestroy => Option::from(Self::Destroy(winevent, window)),
WinEvent::ObjectCloaked | WinEvent::ObjectHide => {
Option::from(Self::Hide(winevent, window))
}
WinEvent::ObjectHide => Option::from(Self::Hide(winevent, window)),
WinEvent::ObjectCloaked => Option::from(Self::Cloak(winevent, window)),
WinEvent::SystemMinimizeStart => Option::from(Self::Minimize(winevent, window)),
WinEvent::ObjectShow | WinEvent::ObjectUncloaked | WinEvent::SystemMinimizeEnd => {
WinEvent::ObjectShow | WinEvent::SystemMinimizeEnd => {
Option::from(Self::Show(winevent, window))
}
WinEvent::ObjectUncloaked => Option::from(Self::Uncloak(winevent, window)),
WinEvent::ObjectFocus | WinEvent::SystemForeground => {
Option::from(Self::FocusChange(winevent, window))
}
@@ -147,17 +154,6 @@ impl WindowManagerEvent {
None
}
}
WinEvent::ObjectCreate => {
if let Ok(title) = window.title() {
// Hidden COM support mechanism window that fires this event on both DPI/scaling
// changes and resolution changes, a good candidate for polling
if title == "OLEChannelWnd" {
return Option::from(Self::MonitorPoll(winevent, window));
}
}
None
}
_ => None,
}
}

View File

@@ -1,6 +1,9 @@
use std::collections::VecDeque;
use std::convert::TryFrom;
use std::ffi::c_void;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::eyre::Error;
@@ -9,16 +12,16 @@ use windows::core::Result as WindowsCrateResult;
use windows::core::PCSTR;
use windows::core::PWSTR;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::COLORREF;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::Foundation::HMODULE;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::POINT;
use windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::WPARAM;
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DwmSetWindowAttribute;
use windows::Win32::Graphics::Dwm::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;
@@ -28,13 +31,14 @@ 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::InvalidateRect;
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::MONITORINFOEXW;
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
@@ -48,10 +52,20 @@ use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
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::GetKeyState;
use windows::Win32::UI::Input::KeyboardAndMouse::SendInput;
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0;
use windows::Win32::UI::Input::KeyboardAndMouse::INPUT_MOUSE;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTDOWN;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEEVENTF_LEFTUP;
use windows::Win32::UI::Input::KeyboardAndMouse::MOUSEINPUT;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_MENU;
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
@@ -66,24 +80,26 @@ use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::PostMessageW;
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::SetLayeredWindowAttributes;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
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::LWA_ALPHA;
use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY;
use windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
@@ -97,8 +113,12 @@ use windows::Win32::UI::WindowsAndMessaging::SW_SHOWNOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use windows::Win32::UI::WindowsAndMessaging::WM_CLOSE;
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
@@ -113,6 +133,8 @@ use crate::monitor::Monitor;
use crate::ring::Ring;
use crate::set_window_position::SetWindowPosition;
use crate::windows_callbacks;
use crate::BORDER_HWND;
use crate::TRANSPARENCY_COLOUR;
pub enum WindowsResult<T, E> {
Err(E),
@@ -187,24 +209,17 @@ impl WindowsApi {
callback: MONITORENUMPROC,
callback_data_address: isize,
) -> Result<()> {
unsafe {
EnumDisplayMonitors(
HDC(0),
std::ptr::null_mut(),
callback,
LPARAM(callback_data_address),
)
}
.ok()
.process()
unsafe { EnumDisplayMonitors(HDC(0), None, callback, LPARAM(callback_data_address)) }
.ok()
.process()
}
pub fn valid_hmonitors() -> Result<Vec<isize>> {
let mut monitors: Vec<isize> = vec![];
let monitors_ref: &mut Vec<isize> = monitors.as_mut();
pub fn valid_hmonitors() -> Result<Vec<(String, isize)>> {
let mut monitors: Vec<(String, isize)> = vec![];
let monitors_ref: &mut Vec<(String, isize)> = monitors.as_mut();
Self::enum_display_monitors(
Option::Some(windows_callbacks::valid_display_monitors),
monitors_ref as *mut Vec<isize> as isize,
monitors_ref as *mut Vec<(String, isize)> as isize,
)?;
Ok(monitors)
@@ -225,7 +240,7 @@ impl WindowsApi {
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
for monitor in monitors.elements_mut() {
let monitor_id = monitor.id();
let monitor_name = monitor.name().clone();
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
// EnumWindows will enumerate through windows on all monitors
Self::enum_windows(
@@ -243,7 +258,7 @@ impl WindowsApi {
for container in workspace.containers_mut() {
for window in container.windows() {
if Self::monitor_from_window(window.hwnd()) != monitor_id {
if Self::monitor_name_from_window(window.hwnd())? != monitor_name {
windows_on_other_monitors.push(window.hwnd().0);
}
}
@@ -270,6 +285,16 @@ impl WindowsApi {
unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }.0
}
pub fn monitor_name_from_window(hwnd: HWND) -> Result<String> {
// MONITOR_DEFAULTTONEAREST ensures that the return value will never be NULL
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
Ok(
Self::monitor(unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST) }.0)?
.name()
.to_string(),
)
}
pub fn monitor_from_point(point: POINT) -> isize {
// MONITOR_DEFAULTTONEAREST ensures that the return value will never be NULL
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-monitorfromwindow
@@ -279,10 +304,21 @@ impl WindowsApi {
pub fn position_window(hwnd: HWND, layout: &Rect, top: bool) -> Result<()> {
let flags = SetWindowPosition::NO_ACTIVATE;
let position = if top { HWND_TOPMOST } else { HWND_NOTOPMOST };
let position = if top { HWND_TOPMOST } else { HWND_BOTTOM };
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
pub fn bring_window_to_top(hwnd: HWND) -> Result<()> {
unsafe { BringWindowToTop(hwnd) }.ok().process()
}
pub fn raise_window(hwnd: HWND) -> Result<()> {
let flags = SetWindowPosition::NO_MOVE;
let position = HWND_TOPMOST;
Self::set_window_pos(hwnd, &Rect::default(), 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
@@ -290,7 +326,7 @@ impl WindowsApi {
SetWindowPosition::NO_ACTIVATE
};
let position = HWND_BOTTOM;
let position = HWND_NOTOPMOST;
Self::set_window_pos(hwnd, layout, position, flags.bits())
}
@@ -327,6 +363,19 @@ impl WindowsApi {
Self::show_window(hwnd, SW_MINIMIZE);
}
fn post_message(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> Result<()> {
unsafe { PostMessageW(hwnd, message, wparam, lparam) }
.ok()
.process()
}
pub fn close_window(hwnd: HWND) -> Result<()> {
match Self::post_message(hwnd, WM_CLOSE, WPARAM(0), LPARAM(0)) {
Ok(_) => Ok(()),
Err(_) => Err(anyhow!("could not close window")),
}
}
pub fn hide_window(hwnd: HWND) {
Self::show_window(hwnd, SW_HIDE);
}
@@ -416,7 +465,9 @@ impl WindowsApi {
// Behaviour is undefined if an invalid HWND is given
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
let thread_id = unsafe { GetWindowThreadProcessId(hwnd, &mut process_id) };
let thread_id = unsafe {
GetWindowThreadProcessId(hwnd, Option::from(std::ptr::addr_of_mut!(process_id)))
};
(process_id, thread_id)
}
@@ -517,12 +568,7 @@ impl WindowsApi {
let text_ptr = path.as_mut_ptr();
unsafe {
QueryFullProcessImageNameW(
handle,
PROCESS_NAME_WIN32,
PWSTR(text_ptr),
std::ptr::addr_of_mut!(len),
)
QueryFullProcessImageNameW(handle, PROCESS_NAME_WIN32, PWSTR(text_ptr), &mut len)
}
.ok()
.process()?;
@@ -566,14 +612,6 @@ impl WindowsApi {
Ok(())
}
#[allow(dead_code)]
pub fn window_rect_with_extended_frame_bounds(hwnd: HWND) -> Result<Rect> {
let mut rect = RECT::default();
Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect)?;
Ok(Rect::from(rect))
}
pub fn is_window_cloaked(hwnd: HWND) -> Result<bool> {
let mut cloaked: u32 = 0;
Self::dwm_get_window_attribute(hwnd, DWMWA_CLOAKED, &mut cloaked)?;
@@ -596,24 +634,30 @@ impl WindowsApi {
unsafe { IsIconic(hwnd) }.into()
}
pub fn monitor_info_w(hmonitor: HMONITOR) -> Result<MONITORINFO> {
let mut monitor_info: MONITORINFO = unsafe { std::mem::zeroed() };
monitor_info.cbSize = u32::try_from(std::mem::size_of::<MONITORINFO>())?;
unsafe { GetMonitorInfoW(hmonitor, std::ptr::addr_of_mut!(monitor_info).cast()) }
pub fn monitor_info_w(hmonitor: HMONITOR) -> Result<MONITORINFOEXW> {
let mut ex_info = MONITORINFOEXW::default();
ex_info.monitorInfo.cbSize = u32::try_from(std::mem::size_of::<MONITORINFOEXW>())?;
unsafe { GetMonitorInfoW(hmonitor, &mut ex_info.monitorInfo) }
.ok()
.process()?;
Ok(monitor_info)
Ok(ex_info)
}
pub fn monitor(hmonitor: isize) -> Result<Monitor> {
let monitor_info = Self::monitor_info_w(HMONITOR(hmonitor))?;
let ex_info = Self::monitor_info_w(HMONITOR(hmonitor))?;
let name = OsString::from_wide(&ex_info.szDevice);
let name = name
.to_string_lossy()
.replace('\u{0000}', "")
.trim_start_matches(r"\\.\")
.to_string();
Ok(monitor::new(
hmonitor,
monitor_info.rcMonitor.into(),
monitor_info.rcWork.into(),
ex_info.monitorInfo.rcMonitor.into(),
ex_info.monitorInfo.rcWork.into(),
name,
))
}
@@ -630,7 +674,7 @@ impl WindowsApi {
pv_param: *mut c_void,
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
) -> Result<()> {
unsafe { SystemParametersInfoW(action, ui_param, pv_param, update_flags) }
unsafe { SystemParametersInfoW(action, ui_param, Option::from(pv_param), update_flags) }
.ok()
.process()
}
@@ -669,12 +713,12 @@ impl WindowsApi {
)
}
pub fn module_handle_w() -> Result<HINSTANCE> {
pub fn module_handle_w() -> Result<HMODULE> {
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 create_solid_brush(colour: u32) -> HBRUSH {
unsafe { CreateSolidBrush(COLORREF(colour)) }
}
pub fn register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
@@ -692,16 +736,6 @@ impl WindowsApi {
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;
@@ -716,10 +750,10 @@ impl WindowsApi {
.process()
}
pub fn create_border_window(name: PCSTR, instance: HINSTANCE) -> Result<isize> {
pub fn create_border_window(name: PCSTR, instance: HMODULE) -> Result<isize> {
unsafe {
CreateWindowExA(
WS_EX_TOOLWINDOW,
let hwnd = CreateWindowExA(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
name,
name,
WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
@@ -730,9 +764,90 @@ impl WindowsApi {
None,
None,
instance,
std::ptr::null(),
None,
);
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY);
hwnd
}
.process()
}
pub fn set_transparent(hwnd: HWND) {
unsafe {
#[allow(clippy::cast_sign_loss)]
// TODO: alpha should be configurable
SetLayeredWindowAttributes(hwnd, COLORREF(-1i32 as u32), 150, LWA_ALPHA);
}
}
pub fn create_hidden_window(name: PCSTR, instance: HMODULE) -> Result<isize> {
unsafe {
CreateWindowExA(
WS_EX_NOACTIVATE,
name,
name,
WS_DISABLED,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
None,
None,
instance,
None,
)
}
.process()
}
pub fn invalidate_border_rect() -> Result<()> {
unsafe { InvalidateRect(HWND(BORDER_HWND.load(Ordering::SeqCst)), None, false) }
.ok()
.process()
}
pub fn alt_is_pressed() -> bool {
let state = unsafe { GetKeyState(i32::from(VK_MENU.0)) };
#[allow(clippy::cast_sign_loss)]
let actual = (state as u16) & 0x8000;
actual != 0
}
pub fn left_click() -> u32 {
let inputs = [
INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 {
mi: MOUSEINPUT {
dx: 0,
dy: 0,
mouseData: 0,
dwFlags: MOUSEEVENTF_LEFTDOWN,
time: 0,
dwExtraInfo: 0,
},
},
},
INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 {
mi: MOUSEINPUT {
dx: 0,
dy: 0,
mouseData: 0,
dwFlags: MOUSEEVENTF_LEFTUP,
time: 0,
dwExtraInfo: 0,
},
},
},
];
#[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
unsafe {
SendInput(&inputs, std::mem::size_of::<INPUT>() as i32)
}
}
}

View File

@@ -1,19 +1,34 @@
use std::collections::VecDeque;
use std::sync::atomic::Ordering;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::COLORREF;
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::BeginPaint;
use windows::Win32::Graphics::Gdi::CreatePen;
use windows::Win32::Graphics::Gdi::EndPaint;
use windows::Win32::Graphics::Gdi::Rectangle;
use windows::Win32::Graphics::Gdi::SelectObject;
use windows::Win32::Graphics::Gdi::ValidateRect;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::PAINTSTRUCT;
use windows::Win32::Graphics::Gdi::PS_SOLID;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW;
use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage;
use windows::Win32::UI::WindowsAndMessaging::DBT_DEVNODES_CHANGED;
use windows::Win32::UI::WindowsAndMessaging::SPI_ICONVERTICALSPACING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETWORKAREA;
use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::WM_DEVICECHANGE;
use windows::Win32::UI::WindowsAndMessaging::WM_DISPLAYCHANGE;
use windows::Win32::UI::WindowsAndMessaging::WM_PAINT;
use windows::Win32::UI::WindowsAndMessaging::WM_SETTINGCHANGE;
use crate::container::Container;
use crate::monitor::Monitor;
@@ -22,6 +37,11 @@ use crate::window::Window;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::winevent_listener::WINEVENT_CALLBACK_CHANNEL;
use crate::BORDER_COLOUR_CURRENT;
use crate::BORDER_RECT;
use crate::BORDER_WIDTH;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::TRANSPARENCY_COLOUR;
pub extern "system" fn valid_display_monitors(
hmonitor: HMONITOR,
@@ -29,8 +49,11 @@ pub extern "system" fn valid_display_monitors(
_: *mut RECT,
lparam: LPARAM,
) -> BOOL {
let monitors = unsafe { &mut *(lparam.0 as *mut Vec<isize>) };
monitors.push(hmonitor.0);
let monitors = unsafe { &mut *(lparam.0 as *mut Vec<(String, isize)>) };
if let Ok(m) = WindowsApi::monitor(hmonitor.0) {
monitors.push((m.name().to_string(), hmonitor.0));
}
true.into()
}
@@ -50,7 +73,24 @@ pub extern "system" fn enum_display_monitor(
}
if let Ok(m) = WindowsApi::monitor(hmonitor.0) {
monitors.elements_mut().push_back(m);
let monitor_index_preferences = MONITOR_INDEX_PREFERENCES.lock();
let mut index_preference = None;
for (index, monitor_size) in &*monitor_index_preferences {
if m.size() == monitor_size {
index_preference = Option::from(index);
}
}
if let Some(preference) = index_preference {
let current_len = monitors.elements().len();
if *preference > current_len {
monitors.elements_mut().reserve(1);
}
monitors.elements_mut().insert(*preference, m);
} else {
monitors.elements_mut().push_back(m);
}
}
true.into()
@@ -120,7 +160,22 @@ pub extern "system" fn border_window(
unsafe {
match message as u32 {
WM_PAINT => {
InvalidateRect(window, std::ptr::null(), true);
let border_rect = *BORDER_RECT.lock();
let mut ps = PAINTSTRUCT::default();
let hdc = BeginPaint(window, &mut ps);
let hpen = CreatePen(
PS_SOLID,
BORDER_WIDTH.load(Ordering::SeqCst),
COLORREF(BORDER_COLOUR_CURRENT.load(Ordering::SeqCst)),
);
let hbrush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
SelectObject(hdc, hpen);
SelectObject(hdc, hbrush);
Rectangle(hdc, 0, 0, border_rect.right, border_rect.bottom);
EndPaint(window, &ps);
ValidateRect(window, None);
LRESULT(0)
}
WM_DESTROY => {
@@ -131,3 +186,54 @@ pub extern "system" fn border_window(
}
}
}
pub extern "system" fn hidden_window(
window: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
unsafe {
match message as u32 {
WM_DISPLAYCHANGE => {
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
LRESULT(0)
}
// Added based on this https://stackoverflow.com/a/33762334
WM_SETTINGCHANGE => {
#[allow(clippy::cast_possible_truncation)]
if wparam.0 as u32 == SPI_SETWORKAREA.0
|| wparam.0 as u32 == SPI_ICONVERTICALSPACING.0
{
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
}
LRESULT(0)
}
// Added based on this https://stackoverflow.com/a/33762334
WM_DEVICECHANGE => {
#[allow(clippy::cast_possible_truncation)]
if wparam.0 as u32 == DBT_DEVNODES_CHANGED {
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
WINEVENT_CALLBACK_CHANNEL
.lock()
.0
.send(event_type)
.expect("could not send message on WINEVENT_CALLBACK_CHANNEL");
}
LRESULT(0)
}
_ => DefWindowProcW(window, message, wparam, lparam),
}
}
}

View File

@@ -26,7 +26,7 @@ use crate::INITIAL_CONFIGURATION_LOADED;
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters, JsonSchema)]
pub struct Workspace {
#[getset(set = "pub")]
#[getset(get = "pub", set = "pub")]
name: Option<String>,
containers: Ring<Container>,
#[getset(get = "pub", get_mut = "pub", set = "pub")]
@@ -856,7 +856,9 @@ impl Workspace {
if container.windows().is_empty() {
self.containers_mut().remove(focused_idx);
self.resize_dimensions_mut().remove(focused_idx);
if self.resize_dimensions().get(focused_idx).is_some() {
self.resize_dimensions_mut().remove(focused_idx);
}
} else {
container.load_focused_window();
}

453
komorebic.lib.ahk Normal file
View File

@@ -0,0 +1,453 @@
; Generated by komorebic.exe
Start(ffm, await_configuration, tcp_port) {
RunWait("komorebic.exe start " ffm " --await-configuration " await_configuration " --tcp-port " tcp_port, , "Hide")
}
Stop() {
RunWait("komorebic.exe stop", , "Hide")
}
State() {
RunWait("komorebic.exe state", , "Hide")
}
Query(state_query) {
RunWait("komorebic.exe query " state_query, , "Hide")
}
Subscribe(named_pipe) {
RunWait("komorebic.exe subscribe " named_pipe, , "Hide")
}
Unsubscribe(named_pipe) {
RunWait("komorebic.exe unsubscribe " named_pipe, , "Hide")
}
Log() {
RunWait("komorebic.exe log", , "Hide")
}
QuickSaveResize() {
RunWait("komorebic.exe quick-save-resize", , "Hide")
}
QuickLoadResize() {
RunWait("komorebic.exe quick-load-resize", , "Hide")
}
SaveResize(path) {
RunWait("komorebic.exe save-resize " path, , "Hide")
}
LoadResize(path) {
RunWait("komorebic.exe load-resize " path, , "Hide")
}
Focus(operation_direction) {
RunWait("komorebic.exe focus " operation_direction, , "Hide")
}
Move(operation_direction) {
RunWait("komorebic.exe move " operation_direction, , "Hide")
}
Minimize() {
RunWait("komorebic.exe minimize", , "Hide")
}
Close() {
RunWait("komorebic.exe close", , "Hide")
}
ForceFocus() {
RunWait("komorebic.exe force-focus", , "Hide")
}
CycleFocus(cycle_direction) {
RunWait("komorebic.exe cycle-focus " cycle_direction, , "Hide")
}
CycleMove(cycle_direction) {
RunWait("komorebic.exe cycle-move " cycle_direction, , "Hide")
}
Stack(operation_direction) {
RunWait("komorebic.exe stack " operation_direction, , "Hide")
}
Resize(edge, sizing) {
RunWait("komorebic.exe resize " edge " " sizing, , "Hide")
}
ResizeAxis(axis, sizing) {
RunWait("komorebic.exe resize-axis " axis " " sizing, , "Hide")
}
Unstack() {
RunWait("komorebic.exe unstack", , "Hide")
}
CycleStack(cycle_direction) {
RunWait("komorebic.exe cycle-stack " cycle_direction, , "Hide")
}
MoveToMonitor(target) {
RunWait("komorebic.exe move-to-monitor " target, , "Hide")
}
CycleMoveToMonitor(cycle_direction) {
RunWait("komorebic.exe cycle-move-to-monitor " cycle_direction, , "Hide")
}
MoveToWorkspace(target) {
RunWait("komorebic.exe move-to-workspace " target, , "Hide")
}
MoveToNamedWorkspace(workspace) {
RunWait("komorebic.exe move-to-named-workspace " workspace, , "Hide")
}
CycleMoveToWorkspace(cycle_direction) {
RunWait("komorebic.exe cycle-move-to-workspace " cycle_direction, , "Hide")
}
SendToMonitor(target) {
RunWait("komorebic.exe send-to-monitor " target, , "Hide")
}
CycleSendToMonitor(cycle_direction) {
RunWait("komorebic.exe cycle-send-to-monitor " cycle_direction, , "Hide")
}
SendToWorkspace(target) {
RunWait("komorebic.exe send-to-workspace " target, , "Hide")
}
SendToNamedWorkspace(workspace) {
RunWait("komorebic.exe send-to-named-workspace " workspace, , "Hide")
}
CycleSendToWorkspace(cycle_direction) {
RunWait("komorebic.exe cycle-send-to-workspace " cycle_direction, , "Hide")
}
SendToMonitorWorkspace(target_monitor, target_workspace) {
RunWait("komorebic.exe send-to-monitor-workspace " target_monitor " " target_workspace, , "Hide")
}
FocusMonitor(target) {
RunWait("komorebic.exe focus-monitor " target, , "Hide")
}
FocusWorkspace(target) {
RunWait("komorebic.exe focus-workspace " target, , "Hide")
}
FocusMonitorWorkspace(target_monitor, target_workspace) {
RunWait("komorebic.exe focus-monitor-workspace " target_monitor " " target_workspace, , "Hide")
}
FocusNamedWorkspace(workspace) {
RunWait("komorebic.exe focus-named-workspace " workspace, , "Hide")
}
CycleMonitor(cycle_direction) {
RunWait("komorebic.exe cycle-monitor " cycle_direction, , "Hide")
}
CycleWorkspace(cycle_direction) {
RunWait("komorebic.exe cycle-workspace " cycle_direction, , "Hide")
}
MoveWorkspaceToMonitor(target) {
RunWait("komorebic.exe move-workspace-to-monitor " target, , "Hide")
}
NewWorkspace() {
RunWait("komorebic.exe new-workspace", , "Hide")
}
ResizeDelta(pixels) {
RunWait("komorebic.exe resize-delta " pixels, , "Hide")
}
InvisibleBorders(left, top, right, bottom) {
RunWait("komorebic.exe invisible-borders " left " " top " " right " " bottom, , "Hide")
}
GlobalWorkAreaOffset(left, top, right, bottom) {
RunWait("komorebic.exe global-work-area-offset " left " " top " " right " " bottom, , "Hide")
}
MonitorWorkAreaOffset(monitor, left, top, right, bottom) {
RunWait("komorebic.exe monitor-work-area-offset " monitor " " left " " top " " right " " bottom, , "Hide")
}
AdjustContainerPadding(sizing, adjustment) {
RunWait("komorebic.exe adjust-container-padding " sizing " " adjustment, , "Hide")
}
AdjustWorkspacePadding(sizing, adjustment) {
RunWait("komorebic.exe adjust-workspace-padding " sizing " " adjustment, , "Hide")
}
ChangeLayout(default_layout) {
RunWait("komorebic.exe change-layout " default_layout, , "Hide")
}
LoadCustomLayout(path) {
RunWait("komorebic.exe load-custom-layout " path, , "Hide")
}
FlipLayout(axis) {
RunWait("komorebic.exe flip-layout " axis, , "Hide")
}
Promote() {
RunWait("komorebic.exe promote", , "Hide")
}
PromoteFocus() {
RunWait("komorebic.exe promote-focus", , "Hide")
}
Retile() {
RunWait("komorebic.exe retile", , "Hide")
}
MonitorIndexPreference(index_preference, left, top, right, bottom) {
RunWait("komorebic.exe monitor-index-preference " index_preference " " left " " top " " right " " bottom, , "Hide")
}
EnsureWorkspaces(monitor, workspace_count) {
RunWait("komorebic.exe ensure-workspaces " monitor " " workspace_count, , "Hide")
}
EnsureNamedWorkspaces(monitor, names) {
RunWait("komorebic.exe ensure-named-workspaces " monitor " " names, , "Hide")
}
ContainerPadding(monitor, workspace, size) {
RunWait("komorebic.exe container-padding " monitor " " workspace " " size, , "Hide")
}
NamedWorkspaceContainerPadding(workspace, size) {
RunWait("komorebic.exe named-workspace-container-padding " workspace " " size, , "Hide")
}
WorkspacePadding(monitor, workspace, size) {
RunWait("komorebic.exe workspace-padding " monitor " " workspace " " size, , "Hide")
}
NamedWorkspacePadding(workspace, size) {
RunWait("komorebic.exe named-workspace-padding " workspace " " size, , "Hide")
}
WorkspaceLayout(monitor, workspace, value) {
RunWait("komorebic.exe workspace-layout " monitor " " workspace " " value, , "Hide")
}
NamedWorkspaceLayout(workspace, value) {
RunWait("komorebic.exe named-workspace-layout " workspace " " value, , "Hide")
}
WorkspaceCustomLayout(monitor, workspace, path) {
RunWait("komorebic.exe workspace-custom-layout " monitor " " workspace " " path, , "Hide")
}
NamedWorkspaceCustomLayout(workspace, path) {
RunWait("komorebic.exe named-workspace-custom-layout " workspace " " path, , "Hide")
}
WorkspaceLayoutRule(monitor, workspace, at_container_count, layout) {
RunWait("komorebic.exe workspace-layout-rule " monitor " " workspace " " at_container_count " " layout, , "Hide")
}
NamedWorkspaceLayoutRule(workspace, at_container_count, layout) {
RunWait("komorebic.exe named-workspace-layout-rule " workspace " " at_container_count " " layout, , "Hide")
}
WorkspaceCustomLayoutRule(monitor, workspace, at_container_count, path) {
RunWait("komorebic.exe workspace-custom-layout-rule " monitor " " workspace " " at_container_count " " path, , "Hide")
}
NamedWorkspaceCustomLayoutRule(workspace, at_container_count, path) {
RunWait("komorebic.exe named-workspace-custom-layout-rule " workspace " " at_container_count " " path, , "Hide")
}
ClearWorkspaceLayoutRules(monitor, workspace) {
RunWait("komorebic.exe clear-workspace-layout-rules " monitor " " workspace, , "Hide")
}
ClearNamedWorkspaceLayoutRules(workspace) {
RunWait("komorebic.exe clear-named-workspace-layout-rules " workspace, , "Hide")
}
WorkspaceTiling(monitor, workspace, value) {
RunWait("komorebic.exe workspace-tiling " monitor " " workspace " " value, , "Hide")
}
NamedWorkspaceTiling(workspace, value) {
RunWait("komorebic.exe named-workspace-tiling " workspace " " value, , "Hide")
}
WorkspaceName(monitor, workspace, value) {
RunWait("komorebic.exe workspace-name " monitor " " workspace " " value, , "Hide")
}
ToggleWindowContainerBehaviour() {
RunWait("komorebic.exe toggle-window-container-behaviour", , "Hide")
}
TogglePause() {
RunWait("komorebic.exe toggle-pause", , "Hide")
}
ToggleTiling() {
RunWait("komorebic.exe toggle-tiling", , "Hide")
}
ToggleFloat() {
RunWait("komorebic.exe toggle-float", , "Hide")
}
ToggleMonocle() {
RunWait("komorebic.exe toggle-monocle", , "Hide")
}
ToggleMaximize() {
RunWait("komorebic.exe toggle-maximize", , "Hide")
}
RestoreWindows() {
RunWait("komorebic.exe restore-windows", , "Hide")
}
Manage() {
RunWait("komorebic.exe manage", , "Hide")
}
Unmanage() {
RunWait("komorebic.exe unmanage", , "Hide")
}
ReloadConfiguration() {
RunWait("komorebic.exe reload-configuration", , "Hide")
}
WatchConfiguration(boolean_state) {
RunWait("komorebic.exe watch-configuration " boolean_state, , "Hide")
}
CompleteConfiguration() {
RunWait("komorebic.exe complete-configuration", , "Hide")
}
AltFocusHack(boolean_state) {
RunWait("komorebic.exe alt-focus-hack " boolean_state, , "Hide")
}
WindowHidingBehaviour(hiding_behaviour) {
RunWait("komorebic.exe window-hiding-behaviour " hiding_behaviour, , "Hide")
}
CrossMonitorMoveBehaviour(move_behaviour) {
RunWait("komorebic.exe cross-monitor-move-behaviour " move_behaviour, , "Hide")
}
ToggleCrossMonitorMoveBehaviour() {
RunWait("komorebic.exe toggle-cross-monitor-move-behaviour", , "Hide")
}
UnmanagedWindowOperationBehaviour(operation_behaviour) {
RunWait("komorebic.exe unmanaged-window-operation-behaviour " operation_behaviour, , "Hide")
}
FloatRule(identifier, id) {
RunWait("komorebic.exe float-rule " identifier " `"" id "`"", , "Hide")
}
ManageRule(identifier, id) {
RunWait("komorebic.exe manage-rule " identifier " `"" id "`"", , "Hide")
}
WorkspaceRule(identifier, id, monitor, workspace) {
RunWait("komorebic.exe workspace-rule " identifier " `"" id "`" " monitor " " workspace, , "Hide")
}
NamedWorkspaceRule(identifier, id, workspace) {
RunWait("komorebic.exe named-workspace-rule " identifier " `"" id "`" " workspace, , "Hide")
}
IdentifyObjectNameChangeApplication(identifier, id) {
RunWait("komorebic.exe identify-object-name-change-application " identifier " `"" id "`"", , "Hide")
}
IdentifyTrayApplication(identifier, id) {
RunWait("komorebic.exe identify-tray-application " identifier " `"" id "`"", , "Hide")
}
IdentifyLayeredApplication(identifier, id) {
RunWait("komorebic.exe identify-layered-application " identifier " `"" id "`"", , "Hide")
}
IdentifyBorderOverflowApplication(identifier, id) {
RunWait("komorebic.exe identify-border-overflow-application " identifier " `"" id "`"", , "Hide")
}
ActiveWindowBorder(boolean_state) {
RunWait("komorebic.exe active-window-border " boolean_state, , "Hide")
}
ActiveWindowBorderColour(r, g, b, window_kind) {
RunWait("komorebic.exe active-window-border-colour " r " " g " " b " --window-kind " window_kind, , "Hide")
}
ActiveWindowBorderWidth(width) {
RunWait("komorebic.exe active-window-border-width " width, , "Hide")
}
ActiveWindowBorderOffset(offset) {
RunWait("komorebic.exe active-window-border-offset " offset, , "Hide")
}
FocusFollowsMouse(boolean_state, implementation) {
RunWait("komorebic.exe focus-follows-mouse " boolean_state " --implementation " implementation, , "Hide")
}
ToggleFocusFollowsMouse(implementation) {
RunWait("komorebic.exe toggle-focus-follows-mouse --implementation " implementation, , "Hide")
}
MouseFollowsFocus(boolean_state) {
RunWait("komorebic.exe mouse-follows-focus " boolean_state, , "Hide")
}
ToggleMouseFollowsFocus() {
RunWait("komorebic.exe toggle-mouse-follows-focus", , "Hide")
}
AhkLibrary() {
RunWait("komorebic.exe ahk-library", , "Hide")
}
AhkAppSpecificConfiguration(path, override_path) {
RunWait("komorebic.exe ahk-app-specific-configuration " path " " override_path, , "Hide")
}
PwshAppSpecificConfiguration(path, override_path) {
RunWait("komorebic.exe pwsh-app-specific-configuration " path " " override_path, , "Hide")
}
FormatAppSpecificConfiguration(path) {
RunWait("komorebic.exe format-app-specific-configuration " path, , "Hide")
}
NotificationSchema() {
RunWait("komorebic.exe notification-schema", , "Hide")
}
SocketSchema() {
RunWait("komorebic.exe socket-schema", , "Hide")
}

View File

@@ -1,341 +0,0 @@
; Generated by komorebic.exe
Start(ffm, await_configuration) {
Run, komorebic.exe start %ffm% --await_configuration %await_configuration%, , Hide
}
Stop() {
Run, komorebic.exe stop, , Hide
}
State() {
Run, komorebic.exe state, , Hide
}
Query(state_query) {
Run, komorebic.exe query %state_query%, , Hide
}
Subscribe(named_pipe) {
Run, komorebic.exe subscribe %named_pipe%, , Hide
}
Unsubscribe(named_pipe) {
Run, komorebic.exe unsubscribe %named_pipe%, , Hide
}
Log() {
Run, komorebic.exe log, , Hide
}
QuickSaveResize() {
Run, komorebic.exe quick-save-resize, , Hide
}
QuickLoadResize() {
Run, komorebic.exe quick-load-resize, , Hide
}
SaveResize(path) {
Run, komorebic.exe save-resize %path%, , Hide
}
LoadResize(path) {
Run, komorebic.exe load-resize %path%, , Hide
}
Focus(operation_direction) {
Run, komorebic.exe focus %operation_direction%, , Hide
}
Move(operation_direction) {
Run, komorebic.exe move %operation_direction%, , Hide
}
CycleFocus(cycle_direction) {
Run, komorebic.exe cycle-focus %cycle_direction%, , Hide
}
CycleMove(cycle_direction) {
Run, komorebic.exe cycle-move %cycle_direction%, , Hide
}
Stack(operation_direction) {
Run, komorebic.exe stack %operation_direction%, , Hide
}
Resize(edge, sizing) {
Run, komorebic.exe resize %edge% %sizing%, , Hide
}
ResizeAxis(axis, sizing) {
Run, komorebic.exe resize-axis %axis% %sizing%, , Hide
}
Unstack() {
Run, komorebic.exe unstack, , Hide
}
CycleStack(cycle_direction) {
Run, komorebic.exe cycle-stack %cycle_direction%, , Hide
}
MoveToMonitor(target) {
Run, komorebic.exe move-to-monitor %target%, , Hide
}
MoveToWorkspace(target) {
Run, komorebic.exe move-to-workspace %target%, , Hide
}
SendToMonitor(target) {
Run, komorebic.exe send-to-monitor %target%, , Hide
}
SendToWorkspace(target) {
Run, komorebic.exe send-to-workspace %target%, , Hide
}
SendToMonitorWorkspace(target_monitor, target_workspace) {
Run, komorebic.exe send-to-monitor-workspace %target_monitor% %target_workspace%, , Hide
}
FocusMonitor(target) {
Run, komorebic.exe focus-monitor %target%, , Hide
}
FocusWorkspace(target) {
Run, komorebic.exe focus-workspace %target%, , Hide
}
FocusMonitorWorkspace(target_monitor, target_workspace) {
Run, komorebic.exe focus-monitor-workspace %target_monitor% %target_workspace%, , Hide
}
CycleMonitor(cycle_direction) {
Run, komorebic.exe cycle-monitor %cycle_direction%, , Hide
}
CycleWorkspace(cycle_direction) {
Run, komorebic.exe cycle-workspace %cycle_direction%, , Hide
}
MoveWorkspaceToMonitor(target) {
Run, komorebic.exe move-workspace-to-monitor %target%, , Hide
}
NewWorkspace() {
Run, komorebic.exe new-workspace, , Hide
}
ResizeDelta(pixels) {
Run, komorebic.exe resize-delta %pixels%, , Hide
}
InvisibleBorders(left, top, right, bottom) {
Run, komorebic.exe invisible-borders %left% %top% %right% %bottom%, , Hide
}
WorkAreaOffset(left, top, right, bottom) {
Run, komorebic.exe work-area-offset %left% %top% %right% %bottom%, , Hide
}
AdjustContainerPadding(sizing, adjustment) {
Run, komorebic.exe adjust-container-padding %sizing% %adjustment%, , Hide
}
AdjustWorkspacePadding(sizing, adjustment) {
Run, komorebic.exe adjust-workspace-padding %sizing% %adjustment%, , Hide
}
ChangeLayout(default_layout) {
Run, komorebic.exe change-layout %default_layout%, , Hide
}
LoadCustomLayout(path) {
Run, komorebic.exe load-custom-layout %path%, , Hide
}
FlipLayout(axis) {
Run, komorebic.exe flip-layout %axis%, , Hide
}
Promote() {
Run, komorebic.exe promote, , Hide
}
Retile() {
Run, komorebic.exe retile, , Hide
}
EnsureWorkspaces(monitor, workspace_count) {
Run, komorebic.exe ensure-workspaces %monitor% %workspace_count%, , Hide
}
ContainerPadding(monitor, workspace, size) {
Run, komorebic.exe container-padding %monitor% %workspace% %size%, , Hide
}
WorkspacePadding(monitor, workspace, size) {
Run, komorebic.exe workspace-padding %monitor% %workspace% %size%, , Hide
}
WorkspaceLayout(monitor, workspace, value) {
Run, komorebic.exe workspace-layout %monitor% %workspace% %value%, , Hide
}
WorkspaceCustomLayout(monitor, workspace, path) {
Run, komorebic.exe workspace-custom-layout %monitor% %workspace% %path%, , Hide
}
WorkspaceLayoutRule(monitor, workspace, at_container_count, layout) {
Run, komorebic.exe workspace-layout-rule %monitor% %workspace% %at_container_count% %layout%, , Hide
}
WorkspaceCustomLayoutRule(monitor, workspace, at_container_count, path) {
Run, komorebic.exe workspace-custom-layout-rule %monitor% %workspace% %at_container_count% %path%, , Hide
}
ClearWorkspaceLayoutRules(monitor, workspace) {
Run, komorebic.exe clear-workspace-layout-rules %monitor% %workspace%, , Hide
}
WorkspaceTiling(monitor, workspace, value) {
Run, komorebic.exe workspace-tiling %monitor% %workspace% %value%, , Hide
}
WorkspaceName(monitor, workspace, value) {
Run, komorebic.exe workspace-name %monitor% %workspace% %value%, , Hide
}
ToggleWindowContainerBehaviour() {
Run, komorebic.exe toggle-window-container-behaviour, , Hide
}
TogglePause() {
Run, komorebic.exe toggle-pause, , Hide
}
ToggleTiling() {
Run, komorebic.exe toggle-tiling, , Hide
}
ToggleFloat() {
Run, komorebic.exe toggle-float, , Hide
}
ToggleMonocle() {
Run, komorebic.exe toggle-monocle, , Hide
}
ToggleMaximize() {
Run, komorebic.exe toggle-maximize, , Hide
}
RestoreWindows() {
Run, komorebic.exe restore-windows, , Hide
}
Manage() {
Run, komorebic.exe manage, , Hide
}
Unmanage() {
Run, komorebic.exe unmanage, , Hide
}
ReloadConfiguration() {
Run, komorebic.exe reload-configuration, , Hide
}
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
}
CrossMonitorMoveBehaviour(move_behaviour) {
Run, komorebic.exe cross-monitor-move-behaviour %move_behaviour%, , Hide
}
ToggleCrossMonitorMoveBehaviour() {
Run, komorebic.exe toggle-cross-monitor-move-behaviour, , Hide
}
UnmanagedWindowOperationBehaviour(operation_behaviour) {
Run, komorebic.exe unmanaged-window-operation-behaviour %operation_behaviour%, , Hide
}
FloatRule(identifier, id) {
Run, komorebic.exe float-rule %identifier% "%id%", , Hide
}
ManageRule(identifier, id) {
Run, komorebic.exe manage-rule %identifier% "%id%", , Hide
}
WorkspaceRule(identifier, id, monitor, workspace) {
Run, komorebic.exe workspace-rule %identifier% "%id%" %monitor% %workspace%, , Hide
}
IdentifyObjectNameChangeApplication(identifier, id) {
Run, komorebic.exe identify-object-name-change-application %identifier% "%id%", , Hide
}
IdentifyTrayApplication(identifier, id) {
Run, komorebic.exe identify-tray-application %identifier% "%id%", , Hide
}
IdentifyLayeredApplication(identifier, id) {
Run, komorebic.exe identify-layered-application %identifier% "%id%", , Hide
}
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
}
ToggleFocusFollowsMouse(implementation) {
Run, komorebic.exe toggle-focus-follows-mouse --implementation %implementation%, , Hide
}
MouseFollowsFocus(boolean_state) {
Run, komorebic.exe mouse-follows-focus %boolean_state%, , Hide
}
ToggleMouseFollowsFocus() {
Run, komorebic.exe toggle-mouse-follows-focus, , Hide
}
AhkLibrary() {
Run, komorebic.exe ahk-library, , Hide
}
AhkAppSpecificConfiguration(path, override_path) {
Run, komorebic.exe ahk-app-specific-configuration %path% %override_path%, , Hide
}
FormatAppSpecificConfiguration(path) {
Run, komorebic.exe format-app-specific-configuration %path%, , Hide
}
NotificationSchema() {
Run, komorebic.exe notification-schema, , Hide
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.12"
version = "0.1.16"
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"]
@@ -14,9 +14,9 @@ edition = "2021"
derive-ahk = { path = "../derive-ahk" }
komorebi-core = { path = "../komorebi-core" }
clap = { version = "3", features = ["derive", "wrap_help"] }
clap = { version = "4", features = ["derive", "wrap_help"] }
color-eyre = "0.6"
dirs = "4"
dirs = "5"
fs-tail = "0.1"
heck = "0.4"
lazy_static = "1"
@@ -25,10 +25,11 @@ powershell_script = "1.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
sysinfo = "0.29"
uds_windows = "1"
[dependencies.windows]
version = "0.39"
version = "0.48"
features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging"

File diff suppressed because it is too large Load Diff

63
whkdrc.sample Normal file
View File

@@ -0,0 +1,63 @@
.shell powershell
# Reload whkd configuration
# alt + o : taskkill /f /im whkd.exe && start /b whkd # if shell is cmd
alt + o : taskkill /f /im whkd.exe && Start-Process whkd -WindowStyle hidden # if shell is pwsh / powershell
alt + shift + o : komorebic reload-configuration
# App shortcuts - these require shell to be pwsh / powershell
# The apps will be focused if open, or launched if not open
# alt + f : if ($wshell.AppActivate('Firefox') -eq $False) { start firefox }
# alt + b : if ($wshell.AppActivate('Chrome') -eq $False) { start chrome }
# Focus windows
alt + h : komorebic focus left
alt + j : komorebic focus down
alt + k : komorebic focus up
alt + l : komorebic focus right
alt + shift + oem_4 : komorebic cycle-focus previous # oem_4 is [
alt + shift + oem_6 : komorebic cycle-focus next # oem_6 is ]
# Move windows
alt + shift + h : komorebic move left
alt + shift + j : komorebic move down
alt + shift + k : komorebic move up
alt + shift + l : komorebic move right
alt + shift + return : komorebic promote
# Stack windows
alt + left : komorebic stack left
alt + down : komorebic stack down
alt + up : komorebic stack up
alt + right : komorebic stack right
alt + oem_1 : komorebic unstack # oem_1 is ;
alt + oem_4 : komorebic cycle-stack previous # oem_4 is [
alt + oem_6 : komorebic cycle-stack next # oem_6 is ]
# Resize
alt + oem_plus : komorebic resize-axis horizontal increase
alt + oem_minus : komorebic resize-axis horizontal decrease
alt + shift + oem_plus : komorebic resize-axis vertical increase
alt + shift + oem_minus : komorebic resize-axis vertical decrease
# Manipulate windows
alt + t : komorebic toggle-float
alt + shift + f : komorebic toggle-monocle
# Window manager options
alt + shift + r : komorebic retile
alt + p : komorebic toggle-pause
# Layouts
alt + x : komorebic flip-layout horizontal
alt + y : komorebic flip-layout vertical
# Workspaces
alt + 1 : komorebic focus-workspace 0
alt + 2 : komorebic focus-workspace 1
alt + 3 : komorebic focus-workspace 2
# Move windows across workspaces
alt + shift + 1 : komorebic move-to-workspace 0
alt + shift + 2 : komorebic move-to-workspace 1
alt + shift + 3 : komorebic move-to-workspace 2

BIN
wix/License.rtf Normal file

Binary file not shown.

174
wix/main.wxs Normal file
View File

@@ -0,0 +1,174 @@
<?xml version='1.0' encoding='windows-1252'?>
<!--
Copyright (C) 2017 Christopher R. Field.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
The "cargo wix" subcommand provides a variety of predefined variables available
for customization of this template. The values for each variable are set at
installer creation time. The following variables are available:
TargetTriple = The rustc target triple name.
TargetEnv = The rustc target environment. This is typically either
"msvc" or "gnu" depending on the toolchain downloaded and
installed.
TargetVendor = The rustc target vendor. This is typically "pc", but Rust
does support other vendors, like "uwp".
CargoTargetBinDir = The complete path to the binary (exe). The default would
be "target\release\<BINARY_NAME>.exe" where
"<BINARY_NAME>" is replaced with the name of each binary
target defined in the package's manifest (Cargo.toml). If
a different rustc target triple is used than the host,
i.e. cross-compiling, then the default path would be
"target\<CARGO_TARGET>\<CARGO_PROFILE>\<BINARY_NAME>.exe",
where "<CARGO_TARGET>" is replaced with the "CargoTarget"
variable value and "<CARGO_PROFILE>" is replaced with the
value from the `CargoProfile` variable.
CargoTargetDir = The path to the directory for the build artifacts, i.e.
"target".
CargoProfile = Either "debug" or `release` depending on the build
profile. The default is "release".
Version = The version for the installer. The default is the
"Major.Minor.Fix" semantic versioning number of the Rust
package.
-->
<!--
Please do not remove these pre-processor If-Else blocks. These are used with
the `cargo wix` subcommand to automatically determine the installation
destination for 32-bit versus 64-bit installers. Removal of these lines will
cause installation errors.
-->
<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder"?>
<?else ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder"?>
<?endif ?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
<Product Id='*' Name='komorebi' UpgradeCode='F8B967B5-7E7B-4E3A-895B-B789EC898B54' Manufacturer='LGUG2Z' Language='1033' Codepage='1252' Version='$(var.Version)'>
<Package Id='*' Keywords='Installer' Description='A tiling window manager for Windows' Manufacturer='LGUG2Z' InstallerVersion='450' Languages='1033' Compressed='yes' InstallScope='perMachine' SummaryCodepage='1252' />
<MajorUpgrade Schedule='afterInstallInitialize' DowngradeErrorMessage='A newer version of [ProductName] is already installed. Setup will now exit.' />
<Media Id='1' Cabinet='media1.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1' />
<Property Id='DiskPrompt' Value='komorebi Installation' />
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
<Directory Id='APPLICATIONFOLDER' Name='komorebi'>
<!--
Disabling the license sidecar file in the installer is a two step process:
1. Comment out or remove the `Component` tag along with its contents.
2. Comment out or remove the `ComponentRef` tag with the "License" Id
attribute value further down in this file.
-->
<Component Id='License' Guid='*'>
<File Id='LicenseFile' Name='License.rtf' DiskId='1' Source='wix\License.rtf' KeyPath='yes' />
</Component>
<Directory Id='Bin' Name='bin'>
<Component Id='Path' Guid='6C6DF276-06C4-4675-BDED-48C5C2BC9BC5' KeyPath='yes'>
<Environment Id='PATH' Name='PATH' Value='[Bin]' Permanent='no' Part='last' Action='set' System='yes' />
</Component>
<Component Id='binary0' Guid='*'>
<File Id='exe0' Name='komorebi.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebi.exe' KeyPath='yes' />
</Component>
<Component Id='binary1' Guid='*'>
<File Id='exe1' Name='komorebic.exe' DiskId='1' Source='$(var.CargoTargetBinDir)\komorebic.exe' KeyPath='yes' />
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id='Binaries' Title='Application' Description='Installs all binaries and the license.' Level='1' ConfigurableDirectory='APPLICATIONFOLDER' AllowAdvertise='no' Display='expand' Absent='disallow'>
<!--
Comment out or remove the following `ComponentRef` tag to remove
the license sidecar file from the installer.
-->
<ComponentRef Id='License' />
<ComponentRef Id='binary0' />
<ComponentRef Id='binary1' />
<Feature Id='Environment' Title='PATH Environment Variable' Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.' Level='1' Absent='allow'>
<ComponentRef Id='Path' />
</Feature>
</Feature>
<SetProperty Id='ARPINSTALLLOCATION' Value='[APPLICATIONFOLDER]' After='CostFinalize' />
<!--
Uncomment the following `Icon` and `Property` tags to change the product icon.
The product icon is the graphic that appears in the Add/Remove
Programs control panel for the application.
-->
<!--<Icon Id='ProductICO' SourceFile='wix\Product.ico'/>-->
<!--<Property Id='ARPPRODUCTICON' Value='ProductICO' />-->
<Property Id='ARPHELPLINK' Value='https://github.com/LGUG2Z/komorebi' />
<UI>
<UIRef Id='WixUI_FeatureTree' />
<!--
Disabling the EULA dialog in the installer is a two step process:
1. Uncomment the following two `Publish` tags
2. Comment out or remove the `<WiXVariable Id='WixUILicenseRtf'...` tag further down
-->
<!--<Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>-->
<!--<Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>-->
</UI>
<!--
Disabling the EULA dialog in the installer requires commenting out
or removing the following `WixVariable` tag
-->
<WixVariable Id='WixUILicenseRtf' Value='wix\License.rtf' />
<!--
Uncomment the next `WixVaraible` tag to customize the installer's
Graphical User Interface (GUI) and add a custom banner image across
the top of each screen. See the WiX Toolset documentation for details
about customization.
The banner BMP dimensions are 493 x 58 pixels.
-->
<!--<WixVariable Id='WixUIBannerBmp' Value='wix\Banner.bmp'/>-->
<!--
Uncomment the next `WixVariable` tag to customize the installer's
Graphical User Interface (GUI) and add a custom image to the first
dialog, or screen. See the WiX Toolset documentation for details about
customization.
The dialog BMP dimensions are 493 x 312 pixels.
-->
<!--<WixVariable Id='WixUIDialogBmp' Value='wix\Dialog.bmp'/>-->
</Product>
</Wix>