mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-18 06:16:46 +01:00
Compare commits
37 Commits
v0.1.12
...
fix/multip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
106ec88e96 | ||
|
|
0903be7931 | ||
|
|
e78e6b1382 | ||
|
|
adafa32488 | ||
|
|
329b3052a4 | ||
|
|
b25662fea2 | ||
|
|
edc9b0cd3d | ||
|
|
a43eb8fbf5 | ||
|
|
8b0f1d007f | ||
|
|
83a502f199 | ||
|
|
5006aa9009 | ||
|
|
e4a8117a94 | ||
|
|
748c389b34 | ||
|
|
26a18adeb4 | ||
|
|
5d094f601f | ||
|
|
5a0ba4cdbb | ||
|
|
7c41460b14 | ||
|
|
d34a561753 | ||
|
|
04146a3ce9 | ||
|
|
09a544b45b | ||
|
|
0e1ad164d4 | ||
|
|
cec8b04ffd | ||
|
|
f4b3d568ee | ||
|
|
d3dc193d29 | ||
|
|
441bfce053 | ||
|
|
458d1ef80a | ||
|
|
be5945c64b | ||
|
|
38ce38d65c | ||
|
|
6ed52c9387 | ||
|
|
e466a17877 | ||
|
|
f5def84010 | ||
|
|
12473aa41c | ||
|
|
33d1c0edbc | ||
|
|
8d346627d5 | ||
|
|
be83b4b5f2 | ||
|
|
f7ac1d0ece | ||
|
|
2ba3ca4f31 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -13,7 +13,7 @@ updates:
|
||||
- package-ecosystem: "cargo"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
interval: "weekly"
|
||||
assignees:
|
||||
- "LGUG2Z"
|
||||
commit-message:
|
||||
|
||||
29
.github/workflows/windows.yaml
vendored
29
.github/workflows/windows.yaml
vendored
@@ -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,40 @@ 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@latest
|
||||
with:
|
||||
identifier: LGUG2Z.komorebi
|
||||
release-tag: ${{ github.ref }}
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
||||
@@ -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
|
||||
|
||||
85
Cargo.lock
generated
85
Cargo.lock
generated
@@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.16"
|
||||
version = "3.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3dbbb6653e7c55cc8595ad3e1f7be8f32aba4eb7ff7f0fd1163d4f3d137c0a9"
|
||||
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@@ -102,9 +102,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.2.15"
|
||||
version = "3.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ba52acd3b0a5c33aeada5cdaa3267cdc7c594a98731d4268cdc1532f4264cb4"
|
||||
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@@ -202,9 +202,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ctrlc"
|
||||
version = "3.2.2"
|
||||
version = "3.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b37feaa84e6861e00a1f5e5aa8da3ee56d605c9992d33e082786754828e20865"
|
||||
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
|
||||
dependencies = [
|
||||
"nix",
|
||||
"winapi 0.3.9",
|
||||
@@ -212,7 +212,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive-ahk"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -247,9 +247,9 @@ checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
@@ -464,7 +464,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komorebi"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap",
|
||||
@@ -479,6 +479,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"miow 0.4.0",
|
||||
"nanoid",
|
||||
"net2",
|
||||
"os_info",
|
||||
"parking_lot",
|
||||
"paste",
|
||||
@@ -499,7 +500,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komorebi-core"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"color-eyre",
|
||||
@@ -513,7 +514,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "komorebic"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"color-eyre",
|
||||
@@ -528,6 +529,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"sysinfo",
|
||||
"uds_windows",
|
||||
"windows",
|
||||
]
|
||||
@@ -546,9 +548,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.129"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64de3cc433455c14174d42e554d4027ee631c4d046d43e3ecc6efc4636cdc7a7"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
@@ -676,10 +678,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.2"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
|
||||
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
@@ -742,9 +745,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
@@ -759,15 +762,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.2.0"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.4.0"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@@ -797,9 +800,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
|
||||
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
@@ -819,9 +822,9 @@ checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "powershell_script"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5c8599ccb442b80c8c04437bcbcac2f76ff34d7b658b759fb26744dcab6c03"
|
||||
checksum = "54bde2e1a395c0aee9423072d781610da37b7b120edf17d4da99f83d04f2cd54"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
@@ -1037,18 +1040,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.143"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
|
||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.143"
|
||||
version = "1.0.144"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
|
||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1068,9 +1071,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.83"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1079,9 +1082,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.4"
|
||||
version = "0.9.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79b7c9017c64a49806c6e8df8ef99b92446d09c92457f85f91835b01a8064ae0"
|
||||
checksum = "89f31df3f50926cdf2855da5fd8812295c34752cb20438dae42a67f79e021ac3"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itoa",
|
||||
@@ -1155,9 +1158,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sysinfo"
|
||||
version = "0.25.2"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1594a36887d0f70096702bffadfb67dfbfe76ad4bf84605e86157dc9fce9961a"
|
||||
checksum = "4ae2421f3e16b3afd4aa692d23b83d0ba42ee9b0081d5deeb7d21428d7195fb1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"core-foundation-sys",
|
||||
@@ -1252,9 +1255,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.13"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45"
|
||||
checksum = "3c3f9a28b618c3a6b9251b6908e9c99e04b9e5c02e6581ccbb67d59c34ef7f9b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
@@ -1397,13 +1400,13 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.5"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
|
||||
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
152
README.md
152
README.md
@@ -33,6 +33,7 @@ Articles, blog posts, demos, and videos about _komorebi_ can be added to this li
|
||||
|
||||
- [Moving to Windows from Linux Pt 1](https://kvwu.io/posts/moving-to-windows/)
|
||||
- [Windows 下的现代化平铺窗口管理器 komorebi](https://zhuanlan.zhihu.com/p/455064481)
|
||||
- [komorebi を導入してみる](https://zenn.dev/omochice/articles/50f42a3df8f426)
|
||||
|
||||
## GitHub Sponsors Early Access
|
||||
|
||||
@@ -46,6 +47,15 @@ available for free in the public repository once it meets the requisite level of
|
||||
Features-in-progress that are available in early access will be tagged in the issues with
|
||||
an ["early access" label](https://github.com/LGUG2Z/komorebi/issues?q=is%3Aopen+is%3Aissue+label%3A%22early+access%22).
|
||||
|
||||
## Charitable Donations
|
||||
|
||||
`komorebi`, like `vim`, 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.
|
||||
|
||||
## Demonstrations
|
||||
|
||||
[@haxibami](https://github.com/haxibami) showing _komorebi_ running on Windows
|
||||
@@ -119,6 +129,14 @@ using [`setx`](https://docs.microsoft.com/en-us/windows-server/administration/wi
|
||||
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.
|
||||
|
||||
### Winget
|
||||
|
||||
You can use the builtin package manager from Windows to install the latest komorebi release:
|
||||
|
||||
```powershell
|
||||
winget install LGUG2Z.komorebi
|
||||
```
|
||||
|
||||
### Scoop
|
||||
|
||||
If you use the [Scoop](https://scoop.sh/) command line installer, you can run the following commands to install the
|
||||
@@ -127,10 +145,14 @@ binaries from the latest GitHub Release:
|
||||
```powershell
|
||||
scoop bucket add extras
|
||||
scoop install komorebi
|
||||
|
||||
# To download the example configuration
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.sample.ahk -OutFile $Env:USERPROFILE\komorebi.ahk
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebic.lib.ahk -OutFile $Env:USERPROFILE\komorebic.lib.ahk
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.generated.ahk -OutFile $Env:USERPROFILE\komorebi.generated.ahk
|
||||
```
|
||||
|
||||
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.
|
||||
If you install _komorebi_ using Scoop, the binaries will automatically be added to your `Path`.
|
||||
|
||||
Thanks to [@sitiom](https://github.com/sitiom) for getting _komorebi_ added to the popular Scoop Extras bucket.
|
||||
|
||||
@@ -152,7 +174,7 @@ cargo install --path komorebic --locked
|
||||
|
||||
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
|
||||
@@ -264,7 +286,10 @@ If you would like to add a visual border around the currently focused window, tw
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
It is important to note that the active window border will only apply to windows managed by `komorebi`.
|
||||
@@ -480,95 +505,6 @@ You can run `komorebic.exe` to get a full list of the commands that you can use
|
||||
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
|
||||
@@ -598,7 +534,7 @@ used [is available here](komorebi.sample.with.lib.ahk).
|
||||
- [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] Active window border
|
||||
- [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
|
||||
@@ -619,7 +555,7 @@ used [is available here](komorebi.sample.with.lib.ahk).
|
||||
- [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] Configure and compensate for the size of Windows invisible borders
|
||||
- [x] Toggle floating windows
|
||||
- [x] Toggle monocle window
|
||||
- [x] Toggle native maximization
|
||||
@@ -729,9 +665,35 @@ 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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "derive-ahk"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -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
|
||||
Run, komorebic.exe {} {} {}, , Hide
|
||||
}}"#,
|
||||
::std::stringify!(#name),
|
||||
#all_arguments,
|
||||
::std::stringify!(#name).to_kebab_case(),
|
||||
#called_arguments,
|
||||
#flags,
|
||||
#called_flag_arguments
|
||||
#all_flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
7
justfile
7
justfile
@@ -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
|
||||
@@ -19,7 +23,8 @@ install:
|
||||
just install-komorebic
|
||||
just install-komorebi
|
||||
komorebic ahk-library
|
||||
cat '%USERPROFILE%\.config\komorebi\komorebic.lib.ahk' > komorebic.lib.sample.ahk
|
||||
cat '%USERPROFILE%\.config\komorebi\komorebic.lib.ahk' > komorebic.lib.ahk
|
||||
cat '%USERPROFILE%\.config\komorebi\komorebi.generated.ahk' > komorebi.generated.ahk
|
||||
|
||||
run:
|
||||
just install-komorebic
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-core"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -52,6 +52,7 @@ pub enum SocketMessage {
|
||||
SendContainerToMonitorWorkspaceNumber(usize, usize),
|
||||
MoveWorkspaceToMonitorNumber(usize),
|
||||
Promote,
|
||||
PromoteFocus,
|
||||
ToggleFloat,
|
||||
ToggleMonocle,
|
||||
ToggleMaximize,
|
||||
@@ -98,7 +99,7 @@ pub enum SocketMessage {
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
ActiveWindowBorder(bool),
|
||||
ActiveWindowBorderColour(u32, u32, u32),
|
||||
ActiveWindowBorderColour(WindowKind, u32, u32, u32),
|
||||
InvisibleBorders(Rect),
|
||||
WorkAreaOffset(Rect),
|
||||
ResizeDelta(i32),
|
||||
@@ -118,6 +119,7 @@ pub enum SocketMessage {
|
||||
AddSubscriber(String),
|
||||
RemoveSubscriber(String),
|
||||
NotificationSchema,
|
||||
SocketSchema,
|
||||
}
|
||||
|
||||
impl SocketMessage {
|
||||
@@ -134,6 +136,13 @@ impl FromStr for SocketMessage {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum WindowKind {
|
||||
Single,
|
||||
Stack,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum StateQuery {
|
||||
|
||||
348
komorebi.generated.ahk
Normal file
348
komorebi.generated.ahk
Normal file
@@ -0,0 +1,348 @@
|
||||
; Generated by komorebic.exe
|
||||
; To use this file, add the line below to the top of your komorebi.ahk configuration file
|
||||
; #Include %A_ScriptDir%\komorebi.generated.ahk
|
||||
|
||||
; 1Password
|
||||
Run, komorebic.exe float-rule exe "1Password.exe", , Hide
|
||||
|
||||
; Adobe Creative Cloud
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application class "CreativeCloudDesktopWindowClass", , Hide
|
||||
|
||||
; AutoHotkey
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "AutoHotkeyU64.exe", , Hide
|
||||
Run, komorebic.exe float-rule title "Window Spy", , Hide
|
||||
|
||||
; Beeper
|
||||
Run, 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
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Bitwarden.exe", , Hide
|
||||
|
||||
; Calculator
|
||||
Run, 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.
|
||||
Run, komorebic.exe float-rule exe "CredentialUIBroker.exe", , Hide
|
||||
|
||||
; Cron
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Cron.exe", , Hide
|
||||
|
||||
; Discord
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Discord.exe", , Hide
|
||||
|
||||
; DiscordCanary
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "DiscordCanary.exe", , Hide
|
||||
|
||||
; DiscordDevelopment
|
||||
Run, komorebic.exe identify-border-overflow-application exe "DiscordDeveloper.exe", , Hide
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "DiscordDeveloper.exe", , Hide
|
||||
|
||||
; DiscordPTB
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "DiscordPTB.exe", , Hide
|
||||
|
||||
; ElectronMail
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Element.exe", , Hide
|
||||
|
||||
; ElevenClock
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "ElevenClock.exe", , Hide
|
||||
|
||||
; Epic Games Launcher
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "EpicGamesLauncher.exe", , Hide
|
||||
|
||||
; Flow Launcher
|
||||
Run, komorebic.exe identify-border-overflow-application exe "Flow.Launcher.exe", , Hide
|
||||
|
||||
; GOG Galaxy
|
||||
Run, komorebic.exe identify-border-overflow-application exe "GalaxyClient.exe", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "GalaxyClient.exe", , Hide
|
||||
; Targets a hidden window spawned by GOG Galaxy
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application class "GoPro Webcam", , Hide
|
||||
|
||||
; Google Chrome
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "chrome.exe", , Hide
|
||||
|
||||
; Google Drive
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "GoogleDriveFS.exe", , Hide
|
||||
|
||||
; Inno Setup
|
||||
; Target hidden window spawned by Inno Setup applications
|
||||
Run, komorebic.exe float-rule class "TApplication", , Hide
|
||||
; Target Inno Setup installers
|
||||
Run, komorebic.exe float-rule class "TWizardForm", , Hide
|
||||
|
||||
; IntelliJ IDEA
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "idea64.exe", , Hide
|
||||
; Targets JetBrains IDE popups and floating windows
|
||||
Run, komorebic.exe float-rule class "SunAwtDialog", , Hide
|
||||
|
||||
; Kleopatra
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "kleopatra.exe", , Hide
|
||||
|
||||
; Kotatogram
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Kotatogram.exe", , Hide
|
||||
|
||||
; Logi Bolt
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "LogiTune.exe", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "lghub.exe", , Hide
|
||||
Run, komorebic.exe identify-border-overflow-application exe "lghub.exe", , Hide
|
||||
|
||||
; Logitech Options
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "mailspring.exe", , Hide
|
||||
|
||||
; ManyCam
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "ManyCam.exe", , Hide
|
||||
|
||||
; Mica For Everyone
|
||||
|
||||
; Microsoft Excel
|
||||
Run, komorebic.exe identify-border-overflow-application exe "EXCEL.EXE", , Hide
|
||||
Run, komorebic.exe identify-layered-application exe "EXCEL.EXE", , Hide
|
||||
; Targets a hidden window spawned by Microsoft Office applications
|
||||
Run, komorebic.exe float-rule class "_WwB", , Hide
|
||||
|
||||
; Microsoft Outlook
|
||||
Run, komorebic.exe identify-border-overflow-application exe "OUTLOOK.EXE", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "OUTLOOK.EXE", , Hide
|
||||
|
||||
; Microsoft PowerPoint
|
||||
Run, komorebic.exe identify-border-overflow-application exe "POWERPNT.EXE", , Hide
|
||||
Run, komorebic.exe identify-layered-application exe "POWERPNT.EXE", , Hide
|
||||
|
||||
; Microsoft Teams
|
||||
; Target Teams pop-up notification windows
|
||||
Run, komorebic.exe float-rule title "Microsoft Teams Notifications", , Hide
|
||||
|
||||
; Microsoft Word
|
||||
Run, komorebic.exe identify-border-overflow-application exe "WINWORD.EXE", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "ModernFlyoutsHost.exe", , Hide
|
||||
|
||||
; Mozilla Firefox
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "firefox.exe", , Hide
|
||||
; Targets invisible windows spawned by Firefox to show tab previews in the taskbar
|
||||
Run, komorebic.exe float-rule class "MozillaTaskbarPreviewClass", , Hide
|
||||
|
||||
; NVIDIA GeForce Experience
|
||||
Run, komorebic.exe identify-border-overflow-application exe "NVIDIA GeForce Experience.exe", , Hide
|
||||
|
||||
; NiceHash Miner
|
||||
Run, komorebic.exe identify-border-overflow-application exe "nhm_app.exe", , Hide
|
||||
Run, komorebic.exe manage-rule exe "nhm_app.exe", , Hide
|
||||
|
||||
; NohBoard
|
||||
Run, komorebic.exe float-rule exe "NohBoard.exe", , Hide
|
||||
|
||||
; Notion Enhanced
|
||||
Run, 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
|
||||
Run, 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
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "obs64.exe", , Hide
|
||||
|
||||
; ONLYOFFICE Editors
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application class "DocEditorsWindowClass", , Hide
|
||||
|
||||
; Obsidian
|
||||
Run, komorebic.exe identify-border-overflow-application exe "Obsidian.exe", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "OpenRGB.exe", , Hide
|
||||
|
||||
; Paradox Launcher
|
||||
Run, komorebic.exe float-rule exe "Paradox Launcher.exe", , Hide
|
||||
|
||||
; PowerToys
|
||||
; Target color picker dialog
|
||||
Run, komorebic.exe float-rule exe "PowerToys.ColorPickerUI.exe", , Hide
|
||||
; Target image resizer dialog
|
||||
Run, komorebic.exe float-rule exe "PowerToys.ImageResizer.exe", , Hide
|
||||
|
||||
; Process Hacker
|
||||
Run, komorebic.exe identify-border-overflow-application exe "ProcessHacker.exe", , Hide
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "ProcessHacker.exe", , Hide
|
||||
Run, komorebic.exe float-rule exe "ProcessHacker.exe", , Hide
|
||||
|
||||
; ProtonVPN
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "ProtonVPN.exe", , Hide
|
||||
|
||||
; PyCharm
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "pycharm64.exe", , Hide
|
||||
|
||||
; QuickLook
|
||||
Run, komorebic.exe float-rule exe "QuickLook.exe", , Hide
|
||||
|
||||
; RepoZ
|
||||
Run, komorebic.exe float-rule exe "RepoZ.exe", , Hide
|
||||
|
||||
; Roblox FPS Unlocker
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "rbxfpsunlocker.exe", , Hide
|
||||
|
||||
; RoundedTB
|
||||
Run, komorebic.exe float-rule exe "RoundedTB.exe", , Hide
|
||||
|
||||
; RoundedTB
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "RoundedTB.exe", , Hide
|
||||
|
||||
; ShareX
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "ShareX.exe", , Hide
|
||||
|
||||
; Signal
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "sirikali.exe", , Hide
|
||||
|
||||
; Slack
|
||||
Run, komorebic.exe identify-border-overflow-application exe "Slack.exe", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Slack.exe", , Hide
|
||||
|
||||
; Slack
|
||||
Run, komorebic.exe identify-border-overflow-application exe "slack.exe", , Hide
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "slack.exe", , Hide
|
||||
|
||||
; Spotify
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Spotify.exe", , Hide
|
||||
|
||||
; Steam
|
||||
Run, komorebic.exe identify-border-overflow-application class "vguiPopupWindow", , Hide
|
||||
|
||||
; Task Manager
|
||||
Run, komorebic.exe float-rule class "TaskManagerWindow", , Hide
|
||||
|
||||
; Telegram
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "Telegram.exe", , Hide
|
||||
|
||||
; TouchCursor
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, komorebic.exe identify-tray-application exe "tcconfig.exe", , Hide
|
||||
Run, komorebic.exe float-rule exe "tcconfig.exe", , Hide
|
||||
|
||||
; TranslucentTB
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "TranslucentTB.exe", , Hide
|
||||
|
||||
; Unreal Editor
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "UnrealEditor.exe", , Hide
|
||||
|
||||
; Visual Studio Code
|
||||
Run, komorebic.exe identify-border-overflow-application exe "Code.exe", , Hide
|
||||
|
||||
; Windows Console (conhost.exe)
|
||||
Run, komorebic.exe manage-rule class "ConsoleWindowClass", , Hide
|
||||
|
||||
; Windows Explorer
|
||||
; Targets copy/move operation windows
|
||||
Run, komorebic.exe float-rule class "OperationStatusWindow", , Hide
|
||||
Run, komorebic.exe float-rule title "Control Panel", , Hide
|
||||
|
||||
; Windows Installer
|
||||
; Targets MSI Installers
|
||||
Run, komorebic.exe float-rule class "MsiDialogCloseClass", , Hide
|
||||
|
||||
; Wox
|
||||
; Targets a hidden window spawned by Wox
|
||||
Run, komorebic.exe float-rule title "Hotkey sink", , Hide
|
||||
|
||||
; Zoom
|
||||
Run, komorebic.exe float-rule exe "Zoom.exe", , Hide
|
||||
|
||||
; qBittorrent
|
||||
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
|
||||
Run, 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
|
||||
Run, komorebic.exe identify-tray-application exe "ueli.exe", , Hide
|
||||
Run, komorebic.exe float-rule exe "ueli.exe", , Hide
|
||||
15
komorebi.iml
15
komorebi.iml
@@ -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>
|
||||
@@ -1,225 +1,93 @@
|
||||
#SingleInstance Force
|
||||
|
||||
; You can generate a fresh version of this file with "komorebic ahk-library"
|
||||
#Include %A_ScriptDir%\komorebic.lib.ahk
|
||||
; https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations
|
||||
#Include %A_ScriptDir%\komorebi.generated.ahk
|
||||
|
||||
; Default to minimizing windows when switching workspaces
|
||||
WindowHidingBehaviour("minimize")
|
||||
|
||||
; 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")
|
||||
|
||||
; Ensure there is 1 workspace created on monitor 0
|
||||
EnsureWorkspaces(0, 1)
|
||||
|
||||
; 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
|
||||
; Configure the 1st workspace
|
||||
WorkspaceName(0, 0, "I")
|
||||
|
||||
; Ensure there are 3 workspaces created on monitor 0
|
||||
Run, komorebic.exe ensure-workspaces 0 5, , Hide
|
||||
; Uncomment the next two lines if you want a visual border drawn around the focused window
|
||||
; ActiveWindowBorderColour(66, 165, 245) ; this is a nice blue colour
|
||||
; ActiveWindowBorder("enable")
|
||||
|
||||
; 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
|
||||
; Allow komorebi to start managing windows
|
||||
CompleteConfiguration()
|
||||
|
||||
; 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
|
||||
|
||||
; Set the layouts of different workspaces
|
||||
Run, komorebic.exe workspace-layout 0 1 columns, , Hide
|
||||
|
||||
; Set the floaty layout to not tile any windows
|
||||
Run, komorebic.exe workspace-tiling 0 4 disable, , Hide
|
||||
|
||||
; 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
|
||||
|
||||
; 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
|
||||
|
||||
; Always manage forcibly these applications that don't automatically get picked up by komorebi
|
||||
Run, komorebic.exe manage-rule exe TIM.exe, , Hide
|
||||
|
||||
; Identify applications that close to the tray
|
||||
Run, komorebic.exe identify-tray-application exe Discord.exe, , Hide
|
||||
|
||||
; Identify applications that have overflowing borders
|
||||
Run, komorebic.exe identify-border-overflow exe Discord.exe, , Hide
|
||||
|
||||
; Change the focused window, Alt + Vim direction keys
|
||||
; Change the focused window, Alt + Vim direction keys (HJKL)
|
||||
!h::
|
||||
Run, komorebic.exe focus left, , Hide
|
||||
Focus("left")
|
||||
return
|
||||
|
||||
!j::
|
||||
Run, komorebic.exe focus down, , Hide
|
||||
Focus("down")
|
||||
return
|
||||
|
||||
!k::
|
||||
Run, komorebic.exe focus up, , Hide
|
||||
Focus("up")
|
||||
return
|
||||
|
||||
!l::
|
||||
Run, komorebic.exe focus right, , Hide
|
||||
Focus("right")
|
||||
return
|
||||
|
||||
; Move the focused window in a given direction, Alt + Shift + Vim direction keys
|
||||
; Move the focused window in a given direction, Alt + Shift + Vim direction keys (HJKL)
|
||||
!+h::
|
||||
Run, komorebic.exe move left, , Hide
|
||||
Move("left")
|
||||
return
|
||||
|
||||
!+j::
|
||||
Run, komorebic.exe move down, , Hide
|
||||
Move("down")
|
||||
return
|
||||
|
||||
!+k::
|
||||
Run, komorebic.exe move up, , Hide
|
||||
Move("up")
|
||||
return
|
||||
|
||||
!+l::
|
||||
Run, komorebic.exe move right, , Hide
|
||||
Move("right")
|
||||
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
|
||||
; There are many more commands that you can bind to whatever keys combinations you want!
|
||||
;
|
||||
; Have a look at the komorebic.lib.ahk file to see which arguments are required by different commands
|
||||
;
|
||||
; If you want more information about a command, you can run every komorebic command with "--help"
|
||||
;
|
||||
; For example, if you see this in komorebic.lib.ahk
|
||||
;
|
||||
; WorkspaceLayout(monitor, workspace, value) {
|
||||
; Run, komorebic.exe workspace-layout %monitor% %workspace% %value%, , Hide
|
||||
; }
|
||||
;
|
||||
; Just run "komorebic.exe workspace-layout --help" and you'll get all the information you need to use the command
|
||||
;
|
||||
; komorebic.exe-workspace-layout
|
||||
; Set the layout for the specified workspace
|
||||
;
|
||||
; USAGE:
|
||||
; komorebic.exe workspace-layout <MONITOR> <WORKSPACE> <VALUE>
|
||||
;
|
||||
; ARGS:
|
||||
; <MONITOR> Monitor index (zero-indexed)
|
||||
; <WORKSPACE> Workspace index on the specified monitor (zero-indexed)
|
||||
; <VALUE> [possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
;
|
||||
; OPTIONS:
|
||||
; -h, --help Print help information
|
||||
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||
description = "A tiling window manager for Windows"
|
||||
categories = ["tiling-window-manager", "windows"]
|
||||
@@ -25,6 +25,7 @@ hotwatch = "0.4"
|
||||
lazy_static = "1"
|
||||
miow = "0.4"
|
||||
nanoid = "0.4"
|
||||
net2 = "0.2"
|
||||
os_info = "3.4"
|
||||
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
|
||||
paste = "1"
|
||||
@@ -32,7 +33,7 @@ schemars = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
strum = { version = "0.24", features = ["derive"] }
|
||||
sysinfo = "0.25"
|
||||
sysinfo = "0.26"
|
||||
tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
@@ -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,15 @@ 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_OVERFLOW_IDENTIFIERS;
|
||||
use crate::BORDER_RECT;
|
||||
use crate::TRANSPARENCY_COLOUR;
|
||||
use crate::WINDOWS_11;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -40,7 +43,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 +87,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 +100,33 @@ impl Border {
|
||||
invisible_borders: &Rect,
|
||||
activate: bool,
|
||||
) -> Result<()> {
|
||||
let mut should_expand_border = false;
|
||||
if self.hwnd == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
let mut should_expand_border = false;
|
||||
|
||||
let mut rect = WindowsApi::window_rect(window.hwnd())?;
|
||||
rect.top -= invisible_borders.bottom;
|
||||
rect.bottom += invisible_borders.bottom;
|
||||
|
||||
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
|
||||
if border_overflows.contains(&window.title()?)
|
||||
|| border_overflows.contains(&window.exe()?)
|
||||
|| border_overflows.contains(&window.class()?)
|
||||
{
|
||||
should_expand_border = true;
|
||||
}
|
||||
|
||||
if should_expand_border {
|
||||
rect.left -= invisible_borders.left;
|
||||
rect.top -= invisible_borders.top;
|
||||
rect.right += invisible_borders.right;
|
||||
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;
|
||||
}
|
||||
|
||||
*BORDER_RECT.lock() = rect;
|
||||
|
||||
WindowsApi::position_border_window(self.hwnd(), &rect, activate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
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;
|
||||
@@ -38,10 +39,11 @@ use winreg::enums::HKEY_CURRENT_USER;
|
||||
use winreg::RegKey;
|
||||
|
||||
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;
|
||||
@@ -98,11 +100,12 @@ 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 = {
|
||||
@@ -151,6 +154,9 @@ lazy_static! {
|
||||
Version::Semantic(_, _, x) if x >= &22000
|
||||
)
|
||||
};
|
||||
|
||||
static ref BORDER_RECT: Arc<Mutex<Rect>> =
|
||||
Arc::new(Mutex::new(Rect::default()));
|
||||
}
|
||||
|
||||
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||
@@ -159,6 +165,11 @@ pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
pub static BORDER_HWND: AtomicIsize = AtomicIsize::new(0);
|
||||
pub static BORDER_HIDDEN: AtomicBool = AtomicBool::new(false);
|
||||
pub static BORDER_COLOUR_SINGLE: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_STACK: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
|
||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||
|
||||
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
|
||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||
@@ -383,19 +394,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,11 +449,6 @@ 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();
|
||||
|
||||
@@ -446,6 +466,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");
|
||||
});
|
||||
|
||||
@@ -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,6 +42,9 @@ use crate::window_manager::WindowManager;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::Notification;
|
||||
use crate::NotificationEvent;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_COLOUR_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||
@@ -48,6 +57,7 @@ use crate::LAYERED_WHITELIST;
|
||||
use crate::MANAGE_IDENTIFIERS;
|
||||
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 +70,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 +86,45 @@ 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);
|
||||
|
||||
read_commands_tcp(&wm, &mut stream, &addr);
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{}", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
impl WindowManager {
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn process_command(&mut self, message: SocketMessage) -> Result<()> {
|
||||
@@ -107,6 +156,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)?;
|
||||
}
|
||||
@@ -751,6 +801,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,9 +812,18 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
}
|
||||
SocketMessage::NotificationSchema => {
|
||||
let notification = schema_for!(Notification);
|
||||
@@ -769,6 +832,16 @@ impl WindowManager {
|
||||
socket.push("komorebic.sock");
|
||||
let socket = socket.as_path();
|
||||
|
||||
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 = HOME_DIR.clone();
|
||||
socket.push("komorebic.sock");
|
||||
let socket = socket.as_path();
|
||||
|
||||
let mut stream = UnixStream::connect(&socket)?;
|
||||
stream.write_all(schema.as_bytes())?;
|
||||
}
|
||||
@@ -788,7 +861,10 @@ impl WindowManager {
|
||||
| SocketMessage::ToggleMonocle
|
||||
| SocketMessage::ToggleMaximize
|
||||
| SocketMessage::Promote
|
||||
| SocketMessage::PromoteFocus
|
||||
| SocketMessage::Retile
|
||||
| SocketMessage::InvisibleBorders(_)
|
||||
| SocketMessage::WorkAreaOffset(_)
|
||||
| SocketMessage::MoveWindow(_) => {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
@@ -829,32 +905,87 @@ impl WindowManager {
|
||||
tracing::info!("processed");
|
||||
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?)?;
|
||||
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?)?;
|
||||
|
||||
if self.is_paused {
|
||||
return match message {
|
||||
SocketMessage::TogglePause | SocketMessage::State | SocketMessage::Stop => {
|
||||
Ok(self.process_command(message)?)
|
||||
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) {
|
||||
let addr = addr.to_string();
|
||||
let wm = wm.clone();
|
||||
let stream = stream.try_clone().unwrap();
|
||||
std::thread::spawn(move || -> 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(())
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
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(),
|
||||
})?)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.process_command(message.clone())?;
|
||||
notify_subscribers(&serde_json::to_string(&Notification {
|
||||
event: NotificationEvent::Socket(message.clone()),
|
||||
state: self.as_ref().into(),
|
||||
})?)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ 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_SINGLE;
|
||||
use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
@@ -132,7 +135,7 @@ impl WindowManager {
|
||||
|
||||
match event {
|
||||
WindowManagerEvent::Raise(window) => {
|
||||
window.raise()?;
|
||||
window.raise();
|
||||
self.has_pending_raise_op = false;
|
||||
}
|
||||
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
|
||||
@@ -184,22 +187,26 @@ impl WindowManager {
|
||||
}
|
||||
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 +272,7 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowManagerEvent::MoveResizeStart(_, _) => {
|
||||
WindowManagerEvent::MoveResizeStart(_, window) => {
|
||||
let monitor_idx = self.focused_monitor_idx();
|
||||
let workspace_idx = self
|
||||
.focused_monitor()
|
||||
@@ -278,6 +285,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 +304,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,27 +458,28 @@ 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(..) => {}
|
||||
@@ -481,21 +492,72 @@ 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
||||
@@ -187,25 +187,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<()> {
|
||||
@@ -253,6 +283,16 @@ 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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
use uds_windows::UnixListener;
|
||||
|
||||
use crate::border::Border;
|
||||
use komorebi_core::custom_layout::CustomLayout;
|
||||
use komorebi_core::Arrangement;
|
||||
use komorebi_core::Axis;
|
||||
@@ -30,6 +29,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;
|
||||
@@ -205,7 +205,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))]
|
||||
@@ -1046,8 +1047,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 +1253,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()?;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::c_void;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::Error;
|
||||
@@ -28,6 +29,7 @@ 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;
|
||||
@@ -52,6 +54,7 @@ use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
|
||||
use windows::Win32::UI::Shell::Common::DEVICE_SCALE_FACTOR;
|
||||
use windows::Win32::UI::Shell::GetScaleFactorForMonitor;
|
||||
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::BringWindowToTop;
|
||||
use windows::Win32::UI::WindowsAndMessaging::CreateWindowExA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
|
||||
@@ -68,22 +71,22 @@ use windows::Win32::UI::WindowsAndMessaging::IsWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::RegisterClassA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetClassLongPtrW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
|
||||
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
|
||||
use windows::Win32::UI::WindowsAndMessaging::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_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;
|
||||
@@ -99,6 +102,7 @@ use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS
|
||||
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
|
||||
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
|
||||
@@ -113,6 +117,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),
|
||||
@@ -279,10 +285,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 +307,7 @@ impl WindowsApi {
|
||||
SetWindowPosition::NO_ACTIVATE
|
||||
};
|
||||
|
||||
let position = HWND_BOTTOM;
|
||||
let position = HWND_NOTOPMOST;
|
||||
Self::set_window_pos(hwnd, layout, position, flags.bits())
|
||||
}
|
||||
|
||||
@@ -673,8 +690,8 @@ impl WindowsApi {
|
||||
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(colour) }
|
||||
}
|
||||
|
||||
pub fn register_class_a(window_class: &WNDCLASSA) -> Result<u16> {
|
||||
@@ -692,16 +709,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;
|
||||
|
||||
@@ -718,8 +725,8 @@ impl WindowsApi {
|
||||
|
||||
pub fn create_border_window(name: PCSTR, instance: HINSTANCE) -> 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,
|
||||
@@ -731,8 +738,24 @@ impl WindowsApi {
|
||||
None,
|
||||
instance,
|
||||
std::ptr::null(),
|
||||
)
|
||||
);
|
||||
|
||||
SetLayeredWindowAttributes(hwnd, TRANSPARENCY_COLOUR, 0, LWA_COLORKEY);
|
||||
|
||||
hwnd
|
||||
}
|
||||
.process()
|
||||
}
|
||||
|
||||
pub fn invalidate_border_rect() -> Result<()> {
|
||||
unsafe {
|
||||
InvalidateRect(
|
||||
HWND(BORDER_HWND.load(Ordering::SeqCst)),
|
||||
std::ptr::null(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
.ok()
|
||||
.process()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
@@ -6,9 +7,16 @@ 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;
|
||||
@@ -22,6 +30,9 @@ 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::TRANSPARENCY_COLOUR;
|
||||
|
||||
pub extern "system" fn valid_display_monitors(
|
||||
hmonitor: HMONITOR,
|
||||
@@ -120,7 +131,18 @@ 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, std::ptr::addr_of_mut!(ps).cast());
|
||||
let hpen = CreatePen(PS_SOLID, 20, 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, std::ptr::null());
|
||||
|
||||
LRESULT(0)
|
||||
}
|
||||
WM_DESTROY => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; Generated by komorebic.exe
|
||||
|
||||
Start(ffm, await_configuration) {
|
||||
Run, komorebic.exe start %ffm% --await_configuration %await_configuration%, , Hide
|
||||
Start(ffm, await_configuration, tcp_port) {
|
||||
Run, komorebic.exe start %ffm% --await-configuration %await_configuration% --tcp-port %tcp_port%, , Hide
|
||||
}
|
||||
|
||||
Stop() {
|
||||
@@ -164,6 +164,10 @@ Promote() {
|
||||
Run, komorebic.exe promote, , Hide
|
||||
}
|
||||
|
||||
PromoteFocus() {
|
||||
Run, komorebic.exe promote-focus, , Hide
|
||||
}
|
||||
|
||||
Retile() {
|
||||
Run, komorebic.exe retile, , Hide
|
||||
}
|
||||
@@ -304,8 +308,8 @@ 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
|
||||
ActiveWindowBorderColour(r, g, b, window_kind) {
|
||||
Run, komorebic.exe active-window-border-colour %r% %g% %b% --window-kind %window_kind%, , Hide
|
||||
}
|
||||
|
||||
FocusFollowsMouse(boolean_state, implementation) {
|
||||
@@ -338,4 +342,8 @@ FormatAppSpecificConfiguration(path) {
|
||||
|
||||
NotificationSchema() {
|
||||
Run, komorebic.exe notification-schema, , Hide
|
||||
}
|
||||
|
||||
SocketSchema() {
|
||||
Run, komorebic.exe socket-schema, , Hide
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
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"]
|
||||
@@ -25,6 +25,7 @@ powershell_script = "1.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_yaml = "0.9"
|
||||
sysinfo = "0.26"
|
||||
uds_windows = "1"
|
||||
|
||||
[dependencies.windows]
|
||||
|
||||
@@ -10,6 +10,7 @@ use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::AppSettings;
|
||||
use clap::ArgEnum;
|
||||
@@ -20,6 +21,7 @@ use fs_tail::TailedFile;
|
||||
use heck::ToKebabCase;
|
||||
use lazy_static::lazy_static;
|
||||
use paste::paste;
|
||||
use sysinfo::SystemExt;
|
||||
use uds_windows::UnixListener;
|
||||
use uds_windows::UnixStream;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
@@ -43,6 +45,7 @@ use komorebi_core::Rect;
|
||||
use komorebi_core::Sizing;
|
||||
use komorebi_core::SocketMessage;
|
||||
use komorebi_core::StateQuery;
|
||||
use komorebi_core::WindowKind;
|
||||
|
||||
lazy_static! {
|
||||
static ref HOME_DIR: PathBuf = {
|
||||
@@ -401,6 +404,8 @@ struct ActiveWindowBorder {
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct ActiveWindowBorderColour {
|
||||
#[clap(arg_enum, short, long, default_value = "single")]
|
||||
window_kind: WindowKind,
|
||||
/// Red
|
||||
r: u32,
|
||||
/// Green
|
||||
@@ -417,6 +422,9 @@ struct Start {
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
@@ -591,6 +599,8 @@ enum SubCommand {
|
||||
FlipLayout(FlipLayout),
|
||||
/// Promote the focused window to the top of the tree
|
||||
Promote,
|
||||
/// Promote the user focus to the top of the tree
|
||||
PromoteFocus,
|
||||
/// Force the retiling of all managed windows
|
||||
Retile,
|
||||
/// Create at least this many workspaces for the specified monitor
|
||||
@@ -710,6 +720,8 @@ enum SubCommand {
|
||||
FormatAppSpecificConfiguration(FormatAppSpecificConfiguration),
|
||||
/// Generate a JSON Schema of subscription notifications
|
||||
NotificationSchema,
|
||||
/// Generate a JSON Schema of socket messages
|
||||
SocketSchema,
|
||||
}
|
||||
|
||||
pub fn send_message(bytes: &[u8]) -> Result<()> {
|
||||
@@ -766,6 +778,9 @@ fn main() -> Result<()> {
|
||||
SubCommand::Promote => {
|
||||
send_message(&SocketMessage::Promote.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::PromoteFocus => {
|
||||
send_message(&SocketMessage::PromoteFocus.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::TogglePause => {
|
||||
send_message(&SocketMessage::TogglePause.as_bytes()?)?;
|
||||
}
|
||||
@@ -937,16 +952,27 @@ fn main() -> Result<()> {
|
||||
|
||||
let script = exec.map_or_else(
|
||||
|| {
|
||||
if arg.ffm | arg.await_configuration {
|
||||
if arg.ffm | arg.await_configuration | arg.tcp_port.is_some() {
|
||||
format!(
|
||||
"Start-Process komorebi.exe -ArgumentList {} -WindowStyle hidden",
|
||||
if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'"
|
||||
} else if arg.ffm {
|
||||
"'--ffm'"
|
||||
} else {
|
||||
"'--await-configuration'"
|
||||
}
|
||||
arg.tcp_port.map_or_else(
|
||||
|| if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'".to_string()
|
||||
} else if arg.ffm {
|
||||
"'--ffm'".to_string()
|
||||
} else {
|
||||
"'--await-configuration'".to_string()
|
||||
},
|
||||
|port| if arg.ffm {
|
||||
format!("'--ffm','--tcp-port={}'", port)
|
||||
} else if arg.await_configuration {
|
||||
format!("'--await-configuration','--tcp-port={}'", port)
|
||||
} else if arg.ffm && arg.await_configuration {
|
||||
format!("'--ffm','--await-configuration','--tcp-port={}'", port)
|
||||
} else {
|
||||
format!("'--tcp-port={}'", port)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
String::from("Start-Process komorebi.exe -WindowStyle hidden")
|
||||
@@ -957,13 +983,24 @@ fn main() -> Result<()> {
|
||||
format!(
|
||||
"Start-Process '{}' -ArgumentList {} -WindowStyle hidden",
|
||||
exec,
|
||||
if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'"
|
||||
} else if arg.ffm {
|
||||
"'--ffm'"
|
||||
} else {
|
||||
"'--await-configuration'"
|
||||
}
|
||||
arg.tcp_port.map_or_else(
|
||||
|| if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'".to_string()
|
||||
} else if arg.ffm {
|
||||
"'--ffm'".to_string()
|
||||
} else {
|
||||
"'--await-configuration'".to_string()
|
||||
},
|
||||
|port| if arg.ffm {
|
||||
format!("'--ffm','--tcp-port={}'", port)
|
||||
} else if arg.await_configuration {
|
||||
format!("'--await-configuration','--tcp-port={}'", port)
|
||||
} else if arg.ffm && arg.await_configuration {
|
||||
format!("'--ffm','--await-configuration','--tcp-port={}'", port)
|
||||
} else {
|
||||
format!("'--tcp-port={}'", port)
|
||||
}
|
||||
)
|
||||
)
|
||||
} else {
|
||||
format!("Start-Process '{}' -WindowStyle hidden", exec)
|
||||
@@ -971,12 +1008,29 @@ fn main() -> Result<()> {
|
||||
},
|
||||
);
|
||||
|
||||
match powershell_script::run(&script) {
|
||||
Ok(output) => {
|
||||
println!("{}", output);
|
||||
let mut running = false;
|
||||
|
||||
while !running {
|
||||
match powershell_script::run(&script) {
|
||||
Ok(_) => {
|
||||
println!("{}", script);
|
||||
}
|
||||
Err(error) => {
|
||||
println!("Error: {}", error);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
println!("Error: {}", error);
|
||||
|
||||
print!("Waiting for komorebi.exe to start...");
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
|
||||
let mut system = sysinfo::System::new_all();
|
||||
system.refresh_processes();
|
||||
|
||||
if system.processes_by_name("komorebi.exe").next().is_some() {
|
||||
println!("Started!");
|
||||
running = true;
|
||||
} else {
|
||||
println!("komorebi.exe did not start... Trying again");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1209,7 +1263,8 @@ fn main() -> Result<()> {
|
||||
}
|
||||
SubCommand::ActiveWindowBorderColour(arg) => {
|
||||
send_message(
|
||||
&SocketMessage::ActiveWindowBorderColour(arg.r, arg.g, arg.b).as_bytes()?,
|
||||
&SocketMessage::ActiveWindowBorderColour(arg.window_kind, arg.r, arg.g, arg.b)
|
||||
.as_bytes()?,
|
||||
)?;
|
||||
}
|
||||
SubCommand::ResizeDelta(arg) => {
|
||||
@@ -1305,6 +1360,40 @@ fn main() -> Result<()> {
|
||||
|
||||
send_message(&SocketMessage::NotificationSchema.as_bytes()?)?;
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
match listener.accept() {
|
||||
Ok(incoming) => {
|
||||
let stream = BufReader::new(incoming.0);
|
||||
for line in stream.lines() {
|
||||
println!("{}", line?);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
Err(error) => {
|
||||
panic!("{}", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
SubCommand::SocketSchema => {
|
||||
let home = HOME_DIR.clone();
|
||||
let mut socket = home;
|
||||
socket.push("komorebic.sock");
|
||||
let socket = socket.as_path();
|
||||
|
||||
match std::fs::remove_file(&socket) {
|
||||
Ok(_) => {}
|
||||
Err(error) => match error.kind() {
|
||||
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
|
||||
ErrorKind::NotFound => {}
|
||||
_ => {
|
||||
return Err(error.into());
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
send_message(&SocketMessage::SocketSchema.as_bytes()?)?;
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
match listener.accept() {
|
||||
Ok(incoming) => {
|
||||
|
||||
BIN
wix/License.rtf
Normal file
BIN
wix/License.rtf
Normal file
Binary file not shown.
174
wix/main.wxs
Normal file
174
wix/main.wxs
Normal 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>
|
||||
Reference in New Issue
Block a user