Compare commits

...

58 Commits

Author SHA1 Message Date
LGUG2Z
ef61239580 chore(release): v0.1.19 2023-11-10 16:23:53 -08:00
LGUG2Z
96bf37b98d fix(config): disable tiling for ws without layouts
This commit ensures that when a layout or a custom layout is not defined
for a workspace in the static configuration file, Workspace.tile will be
set to false. Thanks to M. Kichel on Discord for pointing out the need
for this!
2023-11-10 12:47:21 -08:00
LGUG2Z
5fd90d222d fix(docs): quote $env:userprofile in quickstart to handle spaces 2023-11-08 07:58:25 -08:00
dependabot[bot]
6ffdc1e90e chore(deps): bump which from 4.4.2 to 5.0.0
Bumps [which](https://github.com/harryfei/which-rs) from 4.4.2 to 5.0.0.
- [Changelog](https://github.com/harryfei/which-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/harryfei/which-rs/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-06 11:11:35 -08:00
LGUG2Z
2595fa601f fix(config): typo in layered app id loading 2023-11-04 12:08:36 -07:00
LGUG2Z
41cd5ec222 chore(deps): cargo update 2023-11-03 09:46:37 -07:00
dependabot[bot]
e75ab17602 chore(deps): bump serde from 1.0.188 to 1.0.190
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.188 to 1.0.190.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.188...v1.0.190)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-03 09:29:18 -07:00
dependabot[bot]
79dfa45ec4 chore(deps): bump clap from 4.4.6 to 4.4.7
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.6 to 4.4.7.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.4.6...v4.4.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-11-03 09:29:09 -07:00
dependabot[bot]
8771aab3f3 chore(deps): bump quote from 1.0.30 to 1.0.33 (#573)
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.30 to 1.0.33.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.30...1.0.33)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-03 09:28:47 -07:00
LGUG2Z
f659deb5e3 fix(wm): correct border overflow handling logic
This commit fixes a regression introduced in the regex rule matching
refactor.

Invisible borders should be removed from applications that are not
identified as border overflow applications, not vice versa.

This is because applications that are overflowing their borders
effectively erase the invisible borders, while applications that are not
overflowing their borders leave the system's invisible borders
visibility intact.

It is this latter group that we should be targeting with the
should_remove_border variable.
2023-11-03 09:24:43 -07:00
Kushashwa Ravi Shrimali
e9fb9297e9 feat(cli): add cycle-layout command (#556)
* Command to ToggleLayout

* Just improve logic of figuring out next layout

* Addr review: rename to "Cycle" instead of toggle, and add a small comment

* As per review comments, implement cycle method on DefaultLayout

* I forgot to remove this, my bad

* feat(cli): fixups for cycle-layout cmd

* Update komorebic/src/main.rs

Co-authored-by: Kushashwa Ravi Shrimali <kushashwaravishrimali@gmail.com>

---------

Co-authored-by: LGUG2Z <jadeiqbal@fastmail.com>
Co-authored-by: جاد <LGUG2Z@users.noreply.github.com>
2023-10-26 21:19:24 -07:00
جاد
557d1962a7 Merge pull request #565 from LGUG2Z/dependabot/cargo/proc-macro2-1.0.69
chore(deps): bump proc-macro2 from 1.0.64 to 1.0.69
2023-10-26 16:00:56 -07:00
جاد
d7991d2087 Merge pull request #566 from LGUG2Z/dependabot/cargo/serde_json-1.0.107
chore(deps): bump serde_json from 1.0.103 to 1.0.107
2023-10-26 15:58:33 -07:00
جاد
5606f1c5c4 Merge pull request #567 from LGUG2Z/dependabot/cargo/ctrlc-3.4.1
chore(deps): bump ctrlc from 3.4.0 to 3.4.1
2023-10-26 15:58:17 -07:00
dependabot[bot]
41a627e7dd chore(deps): bump ctrlc from 3.4.0 to 3.4.1
Bumps [ctrlc](https://github.com/Detegr/rust-ctrlc) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/Detegr/rust-ctrlc/releases)
- [Commits](https://github.com/Detegr/rust-ctrlc/compare/3.4.0...3.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 14:25:46 +00:00
dependabot[bot]
2b49ade205 chore(deps): bump serde_json from 1.0.103 to 1.0.107
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.103 to 1.0.107.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.103...v1.0.107)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 14:25:38 +00:00
dependabot[bot]
a0bebc7be8 chore(deps): bump proc-macro2 from 1.0.64 to 1.0.69
Bumps [proc-macro2](https://github.com/dtolnay/proc-macro2) from 1.0.64 to 1.0.69.
- [Release notes](https://github.com/dtolnay/proc-macro2/releases)
- [Commits](https://github.com/dtolnay/proc-macro2/compare/1.0.64...1.0.69)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-23 14:25:29 +00:00
جاد
038256de6b fix(docs): typo whkrc -> whkdrc 2023-10-22 17:34:56 -07:00
sitiom
8c30b894fe ci(winget): change Winget Releaser job to ubuntu-latest (#518) 2023-10-22 10:58:33 -07:00
LGUG2Z
fce86397a5 feat(cli): add autostart commands
This commit introduce two new commands, enable-autostart and
disable-autostart, to help create shortcuts for users in shell:startup
to automatically start komorebi after logging in.
2023-10-22 10:57:05 -07:00
dependabot[bot]
f55b10caa0 chore(deps): bump sysinfo from 0.29.9 to 0.29.10 (#561)
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.29.9 to 0.29.10.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-20 08:24:45 -07:00
dependabot[bot]
ce9d23e72e chore(deps): bump tracing from 0.1.37 to 0.1.39 (#562)
Bumps [tracing](https://github.com/tokio-rs/tracing) from 0.1.37 to 0.1.39.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-0.1.37...tracing-0.1.39)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-20 08:24:33 -07:00
dependabot[bot]
d79e54fad8 chore(deps): bump rustix from 0.37.23 to 0.37.25 (#563)
Bumps [rustix](https://github.com/bytecodealliance/rustix) from 0.37.23 to 0.37.25.
- [Release notes](https://github.com/bytecodealliance/rustix/releases)
- [Commits](https://github.com/bytecodealliance/rustix/compare/v0.37.23...v0.37.25)

---
updated-dependencies:
- dependency-name: rustix
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-20 08:10:44 -07:00
dependabot[bot]
a15c769bfc chore(deps): bump reqwest from 0.11.18 to 0.11.22 (#559)
Bumps [reqwest](https://github.com/seanmonstar/reqwest) from 0.11.18 to 0.11.22.
- [Release notes](https://github.com/seanmonstar/reqwest/releases)
- [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.18...v0.11.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 08:58:59 -07:00
dependabot[bot]
89aa0387d7 chore(deps): bump winreg from 0.50.0 to 0.51.0 (#558)
Bumps [winreg](https://github.com/gentoo90/winreg-rs) from 0.50.0 to 0.51.0.
- [Release notes](https://github.com/gentoo90/winreg-rs/releases)
- [Changelog](https://github.com/gentoo90/winreg-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gentoo90/winreg-rs/compare/v0.50.0...v0.51.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 08:20:04 -07:00
LGUG2Z
1e23a11b94 docs(readme): update badges 2023-10-08 11:11:03 -07:00
Artemiy
e5a8146927 feat(wm): ultrawide layout resizing (#545)
* Allow different resize constraints for layouts

Change Workspace::enforce_resize_constraints to enforce constraints differently for different layouts
Add enforce_no_resize method for all but bsp layout resize_dimensions

* Add resize constraints for UltrawideVerticalStack layout

Add Workspace::enforce_resize_for_ultrawide method to apply resize
constraints for ultrawide vertical stack layout.

* feat(wm): Use resize_dimensions in calculate for ultrawide layout

Add function calculate_ultrawide_adjustment to calculate adjustments for
individual containers in ultrawide vertical stack layout
Refactor ultrawide layout generation in separate function and use
calculated adjustments

* feat(wm): Enable ultrawide layout in DefaultLayout::resize

* feat(wm): refactor ultrawide resize calculation

Add some helper function and descriptive variable names in calculate_ultrawide_adjustment
Apply clippy lints
2023-10-06 17:33:37 -07:00
LGUG2Z
b048e7c3aa feat(rules): implement all matching strategies
This commit ensures that matching strategies can be used wherever
IdWithIdentifier is used, and that they are respected for users opting
to use the static configuration file format.

Some thought and planning needs to go into how this can be backported to
dynamic configurations via the CLI without breaking existing user
configurations.

re #60
2023-10-06 12:14:58 -07:00
dependabot[bot]
3ff3961381 chore(deps): bump clap from 4.4.2 to 4.4.6
Bumps [clap](https://github.com/clap-rs/clap) from 4.4.2 to 4.4.6.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.4.2...v4.4.6)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-02 08:10:15 -07:00
LGUG2Z
0797316ee6 feat(rules): add explicit matching strategies
This commit is the first in a series of commits which will pave the way
for regex rule matching support in komorebi.

For now, in order to maintain backwards compat and not break anything,
all rules without a matching strategy will get assigned as using the
"Legacy" strategy.

This and the "Equals" strategy are the only two which have been
implemented so far.

There should not be any breaking changes in this commit, not any
functionality lost for users with pre-existing configurations.

re #60
2023-09-22 15:05:20 -07:00
LGUG2Z
57cc02f083 fix(config): align border opts in st/dyn configs
This commit aligns the border option naming and arguments between the
dynamic and static configuration approaches.

The previously named border_width and border_offset options in the
static config will be replaced by active_window_border_width and
active_window_border_offset in v0.1.19.

Similarly the option for the offset will now take a single signed
integer, as it does when using the komorebic command.

re #526
2023-09-21 12:32:30 -07:00
LGUG2Z
45912745d6 chore(deps): bump windows crates from 0.48 to 0.51 2023-09-12 09:55:09 -07:00
dependabot[bot]
0088ca8cdb chore(deps): bump serde from 1.0.185 to 1.0.188
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.185 to 1.0.188.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.185...v1.0.188)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 16:36:59 -07:00
dependabot[bot]
6c912b660e chore(deps): bump clap from 4.3.19 to 4.4.2
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.19 to 4.4.2.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.19...v4.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 16:36:50 -07:00
dependabot[bot]
f02b7229fd chore(deps): bump serde_yaml from 0.9.23 to 0.9.25
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.23 to 0.9.25.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.23...0.9.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-11 16:36:41 -07:00
LGUG2Z
ffaab771f3 fix(cli): remedy regression in start cmd
This commit remedies a regression noticed by user @notkvwu in #493,
which results in 'komorebic start' failing, due to an empty ArgumentList
being passed to the PS Start-Process command.

This has been fixed by ensuring that the ArgumentList is only passed
when the user has specified flags on the start command.

fix #493
2023-09-11 13:33:11 -07:00
LGUG2Z
710a8d76ea feat(cli): add ahk flag to static config start cmd
This commit introduces a new --ahk flag to the start command, which
works similarly to the --whkd, and attempts to load an ahk key binding
configuration script on startup.

The attempt is made from within the komorebic process, rather than the
komorebi process.

resolve #529
2023-09-11 11:26:35 -07:00
dependabot[bot]
09792a7924 chore(deps): bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-10 15:13:05 -07:00
dependabot[bot]
c1061dd3be chore(deps): bump serde from 1.0.179 to 1.0.185
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.179 to 1.0.185.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.179...v1.0.185)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-10 15:09:46 -07:00
dependabot[bot]
f8a096090f chore(deps): bump quote from 1.0.29 to 1.0.30
Bumps [quote](https://github.com/dtolnay/quote) from 1.0.29 to 1.0.30.
- [Release notes](https://github.com/dtolnay/quote/releases)
- [Commits](https://github.com/dtolnay/quote/compare/1.0.29...1.0.30)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-10 15:09:39 -07:00
dependabot[bot]
e4ccd45176 chore(deps): bump sysinfo from 0.29.7 to 0.29.9
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.29.7 to 0.29.9.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-10 15:09:31 -07:00
LGUG2Z
f4e0fb9469 feat(cli): add opt to stop whkd alongside wm
This commit adds a '--whkd' flag to the 'komorebic stop' command, which
will also stop any running instance of whkd when stopping the window
manager.

This flag defaults to false, and should not change the existing
behaviour that users are used to.
2023-09-09 22:02:44 -07:00
LGUG2Z
d8892f3ff2 feat(config): add monitor index prefs to static
This commit adds an "monitor_index_preferences" key to the static config
schema, which was missed during the initial rollout of the static
configuration files. To help with testing, these indexes have also been
exposed on the State struct.

resolve #522
2023-09-01 08:59:22 -07:00
dependabot[bot]
fec56b1971 chore(deps): bump clap from 4.3.12 to 4.3.19
Bumps [clap](https://github.com/clap-rs/clap) from 4.3.12 to 4.3.19.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/v4.3.12...v4.3.19)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-10 10:54:43 -07:00
dependabot[bot]
15aedf1ead chore(deps): bump serde from 1.0.171 to 1.0.179
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.171 to 1.0.179.
- [Release notes](https://github.com/serde-rs/serde/releases)
- [Commits](https://github.com/serde-rs/serde/compare/v1.0.171...v1.0.179)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-10 10:54:31 -07:00
dependabot[bot]
86ed7e1297 chore(deps): bump sysinfo from 0.29.4 to 0.29.7
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.29.4 to 0.29.7.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-08-10 10:54:17 -07:00
p1scescom
c132665ac7 fix 2023-08-02 18:22:37 +09:00
Matt Blacker
cfe4062369 fix(config): gen border colours only if set 2023-07-19 08:10:42 -07:00
Matt Blacker
acf201d9bc feat(config): border colours in static config gen 2023-07-19 08:09:16 -07:00
dependabot[bot]
fa4d14799e chore(deps): bump paste from 1.0.13 to 1.0.14
Bumps [paste](https://github.com/dtolnay/paste) from 1.0.13 to 1.0.14.
- [Release notes](https://github.com/dtolnay/paste/releases)
- [Commits](https://github.com/dtolnay/paste/compare/1.0.13...1.0.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 18:10:16 -07:00
dependabot[bot]
6979f4e699 chore(deps): bump serde_json from 1.0.102 to 1.0.103
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.102 to 1.0.103.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.102...v1.0.103)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 18:10:07 -07:00
dependabot[bot]
d3585ad720 chore(deps): bump serde_yaml from 0.9.22 to 0.9.23
Bumps [serde_yaml](https://github.com/dtolnay/serde-yaml) from 0.9.22 to 0.9.23.
- [Release notes](https://github.com/dtolnay/serde-yaml/releases)
- [Commits](https://github.com/dtolnay/serde-yaml/compare/0.9.22...0.9.23)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-17 18:09:58 -07:00
LGUG2Z
e240bc7706 docs(readme): add static conf migration tutorial video 2023-07-15 16:19:45 -07:00
LGUG2Z
0ed732d085 chore(release): v0.1.18 2023-07-14 14:58:58 -07:00
Johnny Nguyen
fa11594103 fix(cli): support for spaces in config path 2023-07-14 14:57:29 -07:00
LGUG2Z
88aaf1f368 docs(readme): add new quickstart video 2023-07-14 11:27:32 -07:00
LGUG2Z
c5bf62fac0 docs(readme): add note on enabling long paths 2023-07-14 11:04:53 -07:00
LGUG2Z
528a0bf9ff docs(readme): fix typo in quickstart 2023-07-14 09:06:58 -07:00
28 changed files with 1795 additions and 673 deletions

View File

@@ -28,7 +28,7 @@ jobs:
target:
- x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Prep cargo dirs
@@ -116,7 +116,7 @@ jobs:
winget:
name: Publish to WinGet
runs-on: windows-latest
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/v')

770
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
[workspace]
resolver = "2"
members = [
"derive-ahk",
"komorebi",
@@ -8,11 +9,11 @@ members = [
]
[workspace.dependencies]
windows-interface = { version = "0.48" }
windows-implement = { version = "0.48" }
windows-interface = { version = "0.51" }
windows-implement = { version = "0.51" }
[workspace.dependencies.windows]
version = "0.48"
version = "0.51"
features = [
"implement",
"Win32_System_Com",

View File

@@ -3,6 +3,9 @@
Tiling Window Management for Windows.
<p>
<a href="https://bdsmovement.net">
<img alt="Stand With Palestine" src="https://img.shields.io/badge/Stand_With_Palestine-%F0%9F%87%B5%F0%9F%87%B8-white">
</a>
<img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/LGUG2Z/komorebi/.github/workflows/windows.yaml">
<img alt="GitHub" src="https://img.shields.io/github/license/LGUG2Z/komorebi">
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/LGUG2Z/komorebi/total">
@@ -16,11 +19,8 @@ Tiling Window Management for Windows.
<a href="https://notado.app/feeds/jado/software-development">
<img alt="Notado Feed" src="https://img.shields.io/badge/Notado-Subscribe-informational">
</a>
<a href="https://jeezy.substack.com">
<img alt="Substack Read" src="https://img.shields.io/badge/Substack-Read-orange">
</a>
<a href="https://twitter.com/LGUG2Z">
<img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/LGUG2Z">
<img alt="Twitter Follow" src="https://img.shields.io/twitter/follow/LGUG2Z?style=flat">
</a>
</p>
@@ -160,6 +160,13 @@ This means that:
### Quickstart
It highly recommended that you enable support for long paths in Windows by running the following command in an
Administrator Terminal before starting with this quickstart guide:
```powershell
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1
```
Make sure that you have either the [Scoop Package Manager](https://scoop.sh) or WinGet installed, then run the following
commands at a PowerShell prompt. If you are using WinGet, make sure that you open a new terminal window or reload your
profile after running the installation steps. Since this is not required when using `scoop`, I personally recommend that
@@ -180,27 +187,27 @@ winget install LGUG2Z.whkd
winget install LGUG2Z.komorebi
# save the example configuration to ~/komorebi.json
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.example.json -OutFile $Env:USERPROFILE\komorebi.example.json
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.19/komorebi.example.json -OutFile "$Env:USERPROFILE\komorebi.json"
# save the latest generated app-specific config tweaks and fixes
komorebic fetch-app-specific-configuration
# ensure the ~/.config folder exists
mkdir $Env:USERPROFILE\.config -ea 0
mkdir "$Env:USERPROFILE\.config" -ea 0
# save the sample whkdrc file with key bindings to ~/.config/whkdrc
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/whkdrc.sample -OutFile $Env:USERPROFILE\.config\whkdrc
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.19/whkdrc.sample -OutFile "$Env:USERPROFILE\.config\whkdrc"
# start komorebi and whkd
komorebic start -c $Env:USERPROFILE\komorebi.json --whkd
komorebic start -c "$Env:USERPROFILE\komorebi.json" --whkd
```
Thanks to [@sitiom](https://github.com/sitiom) for getting _komorebi_ added to both the popular Scoop Extras bucket and
to WinGet.
<!-- You can watch a walkthrough video of this quickstart below on YouTube. -->
You can watch a walkthrough video of this quickstart below on YouTube.
<!-- [![Watch the quickstart walkthrough video](https://img.youtube.com/vi/cBnLIwMtv8g/hqdefault.jpg)](https://www.youtube.com/watch?v=cBnLIwMtv8g) -->
[![Watch the quickstart walkthrough video](https://img.youtube.com/vi/hDDxtvpjpHs/hqdefault.jpg)](https://www.youtube.com/watch?v=hDDxtvpjpHs)
#### Using Autohotkey
@@ -216,13 +223,13 @@ it solely to handle hotkey bindings.
```powershell
# save the latest generated komorebic library to ~/komorebic.lib.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebic.lib.ahk -OutFile $Env:USERPROFILE\komorebic.lib.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.19/komorebic.lib.ahk -OutFile $Env:USERPROFILE\komorebic.lib.ahk
# save the latest generated app-specific config tweaks and fixes to ~/komorebi.generated.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.generated.ahk -OutFile $Env:USERPROFILE\komorebi.generated.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.19/komorebi.generated.ahk -OutFile $Env:USERPROFILE\komorebi.generated.ahk
# save the sample komorebi configuration file to ~/komorebi.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/master/komorebi.sample.ahk -OutFile $Env:USERPROFILE\komorebi.ahk
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.19/komorebi.sample.ahk -OutFile $Env:USERPROFILE\komorebi.ahk
```
### GitHub Releases
@@ -272,7 +279,7 @@ of the window manager. There is a [complete JSON Schema for this configuration f
users with auto-completions in their editors.
If you are running with a dynamic configuration script as recommended in v0.1.16 and earlier, `komorebi` will find the
sample `komorebi.ps1` file in your `$Env:USERPROFILE` directory and automatically load it. This file also starts `whkd` using the sample `whkrc` file
sample `komorebi.ps1` file in your `$Env:USERPROFILE` directory and automatically load it. This file also starts `whkd` using the sample `whkdrc` file
in your `$Env:USERPROFILE\.config` directory.
Alternatively, if you have AutoHotKey installed and a `komorebi.ahk` file in `$Env:UserProfile` directory, `komorebi`
@@ -293,6 +300,8 @@ There are four configuration options that you may need to set yourself, if you m
- The applications.yaml path
- Any individual application rules you have that are not in applications.yaml
[![Watch the tutorial video](https://img.youtube.com/vi/yqCAOJgL3C0/hqdefault.jpg)](https://www.youtube.com/watch?v=yqCAOJgL3C0)
#### Configuration with `komorebic`
As previously mentioned, this project does not handle anything related to keybindings and shortcuts directly. I

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi-core"
version = "0.1.17"
version = "0.1.19"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -130,85 +130,7 @@ impl Arrangement for DefaultLayout {
layouts
}
Self::UltrawideVerticalStack => {
let mut layouts: Vec<Rect> = vec![];
let primary_right = match len {
1 => area.right,
_ => area.right / 2,
};
let secondary_right = match len {
1 => 0,
2 => area.right - primary_right,
_ => (area.right - primary_right) / 2,
};
let (primary_left, secondary_left, stack_left) = match len {
1 => (area.left, 0, 0),
2 => {
let mut primary = area.left + secondary_right;
let mut secondary = area.left;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
primary = area.left;
secondary = area.left + primary_right;
}
_ => {}
}
(primary, secondary, 0)
}
_ => {
let primary = area.left + secondary_right;
let mut secondary = area.left;
let mut stack = area.left + primary_right + secondary_right;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
secondary = area.left + primary_right + secondary_right;
stack = area.left;
}
_ => {}
}
(primary, secondary, stack)
}
};
if len >= 1 {
layouts.push(Rect {
left: primary_left,
top: area.top,
right: primary_right,
bottom: area.bottom,
});
if len >= 2 {
layouts.push(Rect {
left: secondary_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
});
if len > 2 {
layouts.append(&mut rows(
&Rect {
left: stack_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
},
len - 2,
));
}
}
}
layouts
}
Self::UltrawideVerticalStack => ultrawide(area, len, layout_flip, resize_dimensions),
};
dimensions
@@ -586,3 +508,190 @@ fn recursive_fibonacci(
res
}
}
fn calculate_ultrawide_adjustment(resize_dimensions: &[Option<Rect>]) -> Vec<Rect> {
let len = resize_dimensions.len();
let mut result = vec![Rect::default(); len];
match len {
// One container can't be resized
0 | 1 => (),
2 => {
let (primary, secondary) = result.split_at_mut(1);
let primary = &mut primary[0];
let secondary = &mut secondary[0];
// With two containers on screen container 0 is on the right
if let Some(resize_primary) = resize_dimensions[0] {
resize_left(primary, resize_primary.left);
resize_right(secondary, resize_primary.left);
}
if let Some(resize_secondary) = resize_dimensions[1] {
resize_left(primary, resize_secondary.right);
resize_right(secondary, resize_secondary.right);
}
}
_ => {
let (primary, rest) = result.split_at_mut(1);
let (secondary, tertiary) = rest.split_at_mut(1);
let primary = &mut primary[0];
let secondary = &mut secondary[0];
// With three or more containers container 0 is in the center
if let Some(resize_primary) = resize_dimensions[0] {
resize_left(primary, resize_primary.left);
resize_right(primary, resize_primary.right);
resize_right(secondary, resize_primary.left);
for vertical_element in &mut *tertiary {
resize_left(vertical_element, resize_primary.right);
}
}
// Container 1 is on the left
if let Some(resize_secondary) = resize_dimensions[1] {
resize_left(primary, resize_secondary.right);
resize_right(secondary, resize_secondary.right);
}
// Handle stack on the right
for (i, rect) in resize_dimensions[2..].iter().enumerate() {
if let Some(rect) = rect {
resize_right(primary, rect.left);
tertiary
.iter_mut()
.for_each(|vertical_element| resize_left(vertical_element, rect.left));
// Containers in stack except first can be resized up displacing container
// above them
if i != 0 {
resize_bottom(&mut tertiary[i - 1], rect.top);
resize_top(&mut tertiary[i], rect.top);
}
// Containers in stack except last can be resized down displacing container
// below them
if i != tertiary.len() - 1 {
resize_bottom(&mut tertiary[i], rect.bottom);
resize_top(&mut tertiary[i + 1], rect.bottom);
}
}
}
}
};
result
}
fn resize_left(rect: &mut Rect, resize: i32) {
rect.left += resize / 2;
rect.right += -resize / 2;
}
fn resize_right(rect: &mut Rect, resize: i32) {
rect.right += resize / 2;
}
fn resize_top(rect: &mut Rect, resize: i32) {
rect.top += resize / 2;
rect.bottom += -resize / 2;
}
fn resize_bottom(rect: &mut Rect, resize: i32) {
rect.bottom += resize / 2;
}
fn ultrawide(
area: &Rect,
len: usize,
layout_flip: Option<Axis>,
resize_dimensions: &[Option<Rect>],
) -> Vec<Rect> {
let mut layouts: Vec<Rect> = vec![];
let primary_right = match len {
1 => area.right,
_ => area.right / 2,
};
let secondary_right = match len {
1 => 0,
2 => area.right - primary_right,
_ => (area.right - primary_right) / 2,
};
let (primary_left, secondary_left, stack_left) = match len {
1 => (area.left, 0, 0),
2 => {
let mut primary = area.left + secondary_right;
let mut secondary = area.left;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
primary = area.left;
secondary = area.left + primary_right;
}
_ => {}
}
(primary, secondary, 0)
}
_ => {
let primary = area.left + secondary_right;
let mut secondary = area.left;
let mut stack = area.left + primary_right + secondary_right;
match layout_flip {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
secondary = area.left + primary_right + secondary_right;
stack = area.left;
}
_ => {}
}
(primary, secondary, stack)
}
};
if len >= 1 {
layouts.push(Rect {
left: primary_left,
top: area.top,
right: primary_right,
bottom: area.bottom,
});
if len >= 2 {
layouts.push(Rect {
left: secondary_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
});
if len > 2 {
layouts.append(&mut rows(
&Rect {
left: stack_left,
top: area.top,
right: secondary_right,
bottom: area.bottom,
},
len - 2,
));
}
}
}
let adjustment = calculate_ultrawide_adjustment(resize_dimensions);
layouts
.iter_mut()
.zip(adjustment.iter())
.for_each(|(layout, adjustment)| {
layout.top += adjustment.top;
layout.bottom += adjustment.bottom;
layout.left += adjustment.left;
layout.right += adjustment.right;
});
layouts
}

View File

@@ -50,10 +50,22 @@ impl ApplicationOptions {
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct IdWithIdentifier {
pub kind: ApplicationIdentifier,
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub matching_strategy: Option<MatchingStrategy>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub enum MatchingStrategy {
Legacy,
Equals,
StartsWith,
EndsWith,
Contains,
Regex,
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
@@ -62,6 +74,18 @@ pub struct IdWithIdentifierAndComment {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub comment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub matching_strategy: Option<MatchingStrategy>,
}
impl From<IdWithIdentifierAndComment> for IdWithIdentifier {
fn from(value: IdWithIdentifierAndComment) -> Self {
Self {
kind: value.kind,
id: value.id.clone(),
matching_strategy: value.matching_strategy,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]

View File

@@ -20,6 +20,7 @@ pub enum DefaultLayout {
VerticalStack,
HorizontalStack,
UltrawideVerticalStack,
// NOTE: If any new layout is added, please make sure to register the same in `DefaultLayout::cycle`
}
impl DefaultLayout {
@@ -33,7 +34,7 @@ impl DefaultLayout {
sizing: Sizing,
delta: i32,
) -> Option<Rect> {
if !matches!(self, Self::BSP) {
if !matches!(self, Self::BSP) && !matches!(self, Self::UltrawideVerticalStack) {
return None;
};
@@ -125,4 +126,28 @@ impl DefaultLayout {
Option::from(r)
}
}
#[must_use]
pub const fn cycle_next(self) -> Self {
match self {
Self::BSP => Self::Columns,
Self::Columns => Self::Rows,
Self::Rows => Self::VerticalStack,
Self::VerticalStack => Self::HorizontalStack,
Self::HorizontalStack => Self::UltrawideVerticalStack,
Self::UltrawideVerticalStack => Self::BSP,
}
}
#[must_use]
pub const fn cycle_previous(self) -> Self {
match self {
Self::BSP => Self::UltrawideVerticalStack,
Self::UltrawideVerticalStack => Self::HorizontalStack,
Self::HorizontalStack => Self::VerticalStack,
Self::VerticalStack => Self::Rows,
Self::Rows => Self::Columns,
Self::Columns => Self::BSP,
}
}
}

View File

@@ -77,6 +77,7 @@ pub enum SocketMessage {
AdjustContainerPadding(Sizing, i32),
AdjustWorkspacePadding(Sizing, i32),
ChangeLayout(DefaultLayout),
CycleLayout(CycleDirection),
ChangeLayoutCustom(PathBuf),
FlipLayout(Axis),
// Monitor and Workspace Commands
@@ -192,7 +193,17 @@ pub enum StateQuery {
}
#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
Copy,
Clone,
Debug,
Eq,
PartialEq,
Serialize,
Deserialize,
Display,
EnumString,
ValueEnum,
JsonSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum ApplicationIdentifier {

View File

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

View File

@@ -27,6 +27,10 @@ RunWait('komorebic.exe identify-tray-application exe "Akiflow.exe"', , "Hide")
; Android Studio
RunWait('komorebic.exe identify-object-name-change-application exe "studio64.exe"', , "Hide")
; Anki
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "anki.exe"', , "Hide")
; ArmCord
RunWait('komorebic.exe identify-border-overflow-application exe "ArmCord.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -36,6 +40,7 @@ RunWait('komorebic.exe identify-tray-application exe "ArmCord.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "AutoHotkeyU64.exe"', , "Hide")
RunWait('komorebic.exe float-rule title "Window Spy"', , "Hide")
RunWait('komorebic.exe float-rule exe "AutoHotkeyUX.exe"', , "Hide")
; Beeper
RunWait('komorebic.exe identify-border-overflow-application exe "Beeper.exe"', , "Hide")
@@ -52,6 +57,19 @@ RunWait('komorebic.exe float-rule exe "Bloxstrap.exe"', , "Hide")
; Calculator
RunWait('komorebic.exe float-rule title "Calculator"', , "Hide")
; Clash Verge
RunWait('komorebic.exe identify-border-overflow-application exe "Clash Verge.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Clash Verge.exe"', , "Hide")
; Clementine
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "clementine.exe"', , "Hide")
; CopyQ
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "copyq.exe"', , "Hide")
; Credential Manager UI Host
; Targets the Windows popup prompting you for a PIN instead of a password on 1Password etc.
RunWait('komorebic.exe float-rule exe "CredentialUIBroker.exe"', , "Hide")
@@ -91,6 +109,9 @@ RunWait('komorebic.exe identify-border-overflow-application exe "DiscordPTB.exe"
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "DiscordPTB.exe"', , "Hide")
; Docker Desktop
RunWait('komorebic.exe identify-border-overflow-application exe "Docker Desktop.exe"', , "Hide")
; Dropbox
RunWait('komorebic.exe float-rule exe "Dropbox.exe"', , "Hide")
@@ -123,6 +144,13 @@ RunWait('komorebic.exe identify-border-overflow-application exe "EpicGamesLaunch
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "EpicGamesLauncher.exe"', , "Hide")
; Everything
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Everything.exe"', , "Hide")
; Figma
RunWait('komorebic.exe identify-border-overflow-application exe "Figma.exe"', , "Hide")
; Flow Launcher
RunWait('komorebic.exe identify-border-overflow-application exe "Flow.Launcher.exe"', , "Hide")
@@ -143,11 +171,17 @@ RunWait('komorebic.exe identify-border-overflow-application exe "GodotManager.ex
RunWait('komorebic.exe manage-rule exe "GodotManager.exe"', , "Hide")
RunWait('komorebic.exe identify-object-name-change-application exe "GodotManager.exe"', , "Hide")
; Golden Dict
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "GoldenDict.exe"', , "Hide")
; Google Chrome
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "chrome.exe"', , "Hide")
; Google Drive
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "GoogleDriveFS.exe"', , "Hide")
RunWait('komorebic.exe float-rule exe "GoogleDriveFS.exe"', , "Hide")
; Houdoku
@@ -256,6 +290,10 @@ RunWait('komorebic.exe identify-border-overflow-application exe "NVIDIA GeForce
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "NZXT CAM.exe"', , "Hide")
; NetEase Cloud Music
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "cloudmusic.exe"', , "Hide")
; NiceHash Miner
RunWait('komorebic.exe identify-border-overflow-application exe "nhm_app.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "nhm_app.exe"', , "Hide")
@@ -288,6 +326,10 @@ RunWait('komorebic.exe manage-rule exe "Obsidian.exe"', , "Hide")
; OneDrive
RunWait('komorebic.exe float-rule class "OneDriveReactNativeWin32WindowClass"', , "Hide")
; OneQuick
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "OneQuick.exe"', , "Hide")
; OpenRGB
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "OpenRGB.exe"', , "Hide")
@@ -295,6 +337,13 @@ RunWait('komorebic.exe identify-tray-application exe "OpenRGB.exe"', , "Hide")
; Paradox Launcher
RunWait('komorebic.exe float-rule exe "Paradox Launcher.exe"', , "Hide")
; Playnite
RunWait('komorebic.exe identify-border-overflow-application exe "Playnite.DesktopApp.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Playnite.DesktopApp.exe"', , "Hide")
; Target fullscreen app
RunWait('komorebic.exe float-rule exe "Playnite.FullscreenApp.exe"', , "Hide")
; Plexamp
RunWait('komorebic.exe identify-border-overflow-application exe "Plexamp.exe"', , "Hide")
@@ -321,6 +370,13 @@ RunWait('komorebic.exe identify-object-name-change-application exe "pycharm64.ex
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "pycharm64.exe"', , "Hide")
; QQ
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "QQ.exe"', , "Hide")
RunWait('komorebic.exe float-rule title "图片查看器"', , "Hide")
RunWait('komorebic.exe float-rule title "群聊的聊天记录"', , "Hide")
RunWait('komorebic.exe float-rule title "语音通话"', , "Hide")
; QtScrcpy
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "QtScrcpy.exe"', , "Hide")
@@ -348,6 +404,15 @@ RunWait('komorebic.exe identify-border-overflow-application exe "RoundedTB.exe"'
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "RoundedTB.exe"', , "Hide")
; RustRover
RunWait('komorebic.exe identify-object-name-change-application exe "rustrover64.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "rustrover64.exe"', , "Hide")
; Sandboxie Plus
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "SandMan.exe"', , "Hide")
; ShareX
RunWait('komorebic.exe identify-border-overflow-application exe "ShareX.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -358,7 +423,7 @@ RunWait('komorebic.exe float-rule exe "sideloadly.exe"', , "Hide")
; Signal
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "signal.exe"', , "Hide")
RunWait('komorebic.exe identify-tray-application exe "Signal.exe"', , "Hide")
; SiriKali
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -484,6 +549,10 @@ RunWait('komorebic.exe float-rule title "Control Panel"', , "Hide")
; Windows Installer
RunWait('komorebic.exe float-rule exe "msiexec.exe"', , "Hide")
; Windows Subsystem for Android
; Targets splash/startup screen
RunWait('komorebic.exe float-rule class "android(splash)"', , "Hide")
; WingetUI
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "WingetUI.exe"', , "Hide")
@@ -503,6 +572,9 @@ RunWait('komorebic.exe identify-tray-application exe "xampp-control.exe"', , "Hi
; Zoom
RunWait('komorebic.exe float-rule exe "Zoom.exe"', , "Hide")
; mpv
RunWait('komorebic.exe identify-object-name-change-application class "mpv"', , "Hide")
; mpv.net
RunWait('komorebic.exe identify-object-name-change-application exe "mpvnet.exe"', , "Hide")

View File

@@ -27,6 +27,10 @@ komorebic.exe identify-tray-application exe "Akiflow.exe"
# Android Studio
komorebic.exe identify-object-name-change-application exe "studio64.exe"
# Anki
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "anki.exe"
# ArmCord
komorebic.exe identify-border-overflow-application exe "ArmCord.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -36,6 +40,7 @@ komorebic.exe identify-tray-application exe "ArmCord.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "AutoHotkeyU64.exe"
komorebic.exe float-rule title "Window Spy"
komorebic.exe float-rule exe "AutoHotkeyUX.exe"
# Beeper
komorebic.exe identify-border-overflow-application exe "Beeper.exe"
@@ -52,6 +57,19 @@ komorebic.exe float-rule exe "Bloxstrap.exe"
# Calculator
komorebic.exe float-rule title "Calculator"
# Clash Verge
komorebic.exe identify-border-overflow-application exe "Clash Verge.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Clash Verge.exe"
# Clementine
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "clementine.exe"
# CopyQ
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "copyq.exe"
# Credential Manager UI Host
# Targets the Windows popup prompting you for a PIN instead of a password on 1Password etc.
komorebic.exe float-rule exe "CredentialUIBroker.exe"
@@ -91,6 +109,9 @@ komorebic.exe identify-border-overflow-application exe "DiscordPTB.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "DiscordPTB.exe"
# Docker Desktop
komorebic.exe identify-border-overflow-application exe "Docker Desktop.exe"
# Dropbox
komorebic.exe float-rule exe "Dropbox.exe"
@@ -123,6 +144,13 @@ komorebic.exe identify-border-overflow-application exe "EpicGamesLauncher.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "EpicGamesLauncher.exe"
# Everything
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Everything.exe"
# Figma
komorebic.exe identify-border-overflow-application exe "Figma.exe"
# Flow Launcher
komorebic.exe identify-border-overflow-application exe "Flow.Launcher.exe"
@@ -143,11 +171,17 @@ komorebic.exe identify-border-overflow-application exe "GodotManager.exe"
komorebic.exe manage-rule exe "GodotManager.exe"
komorebic.exe identify-object-name-change-application exe "GodotManager.exe"
# Golden Dict
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "GoldenDict.exe"
# Google Chrome
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "chrome.exe"
# Google Drive
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "GoogleDriveFS.exe"
komorebic.exe float-rule exe "GoogleDriveFS.exe"
# Houdoku
@@ -256,6 +290,10 @@ komorebic.exe identify-border-overflow-application exe "NVIDIA GeForce Experienc
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "NZXT CAM.exe"
# NetEase Cloud Music
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "cloudmusic.exe"
# NiceHash Miner
komorebic.exe identify-border-overflow-application exe "nhm_app.exe"
komorebic.exe manage-rule exe "nhm_app.exe"
@@ -288,6 +326,10 @@ komorebic.exe manage-rule exe "Obsidian.exe"
# OneDrive
komorebic.exe float-rule class "OneDriveReactNativeWin32WindowClass"
# OneQuick
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "OneQuick.exe"
# OpenRGB
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "OpenRGB.exe"
@@ -295,6 +337,13 @@ komorebic.exe identify-tray-application exe "OpenRGB.exe"
# Paradox Launcher
komorebic.exe float-rule exe "Paradox Launcher.exe"
# Playnite
komorebic.exe identify-border-overflow-application exe "Playnite.DesktopApp.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Playnite.DesktopApp.exe"
# Target fullscreen app
komorebic.exe float-rule exe "Playnite.FullscreenApp.exe"
# Plexamp
komorebic.exe identify-border-overflow-application exe "Plexamp.exe"
@@ -321,6 +370,13 @@ komorebic.exe identify-object-name-change-application exe "pycharm64.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "pycharm64.exe"
# QQ
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "QQ.exe"
komorebic.exe float-rule title "图片查看器"
komorebic.exe float-rule title "群聊的聊天记录"
komorebic.exe float-rule title "语音通话"
# QtScrcpy
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "QtScrcpy.exe"
@@ -348,6 +404,15 @@ komorebic.exe identify-border-overflow-application exe "RoundedTB.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "RoundedTB.exe"
# RustRover
komorebic.exe identify-object-name-change-application exe "rustrover64.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "rustrover64.exe"
# Sandboxie Plus
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "SandMan.exe"
# ShareX
komorebic.exe identify-border-overflow-application exe "ShareX.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -358,7 +423,7 @@ komorebic.exe float-rule exe "sideloadly.exe"
# Signal
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "signal.exe"
komorebic.exe identify-tray-application exe "Signal.exe"
# SiriKali
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -484,6 +549,10 @@ komorebic.exe float-rule title "Control Panel"
# Windows Installer
komorebic.exe float-rule exe "msiexec.exe"
# Windows Subsystem for Android
# Targets splash/startup screen
komorebic.exe float-rule class "android(splash)"
# WingetUI
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "WingetUI.exe"
@@ -503,6 +572,9 @@ komorebic.exe identify-tray-application exe "xampp-control.exe"
# Zoom
komorebic.exe float-rule exe "Zoom.exe"
# mpv
komorebic.exe identify-object-name-change-application class "mpv"
# mpv.net
komorebic.exe identify-object-name-change-application exe "mpvnet.exe"

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.17"
version = "0.1.19"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -29,6 +29,7 @@ net2 = "0.2"
os_info = "3.7"
parking_lot = { version = "0.12", features = ["deadlock_detection"] }
paste = "1"
regex = "1"
schemars = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
@@ -38,9 +39,9 @@ tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uds_windows = "1"
which = "4"
which = "5"
winput = "0.2"
winreg = "0.50"
winreg = "0.51"
windows-interface = { workspace = true }
windows-implement = { workspace = true }
windows = { workspace = true }

View File

@@ -14,6 +14,7 @@ use windows::Win32::UI::WindowsAndMessaging::WNDCLASSA;
use komorebi_core::Rect;
use crate::window::should_act;
use crate::window::Window;
use crate::windows_callbacks;
use crate::WindowsApi;
@@ -21,6 +22,7 @@ use crate::BORDER_HWND;
use crate::BORDER_OFFSET;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::BORDER_RECT;
use crate::REGEX_IDENTIFIERS;
use crate::TRANSPARENCY_COLOUR;
use crate::WINDOWS_11;
@@ -46,7 +48,7 @@ impl Border {
let class_name = PCSTR(name.as_ptr());
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
let window_class = WNDCLASSA {
hInstance: instance,
hInstance: instance.into(),
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(windows_callbacks::border_window),
@@ -108,19 +110,24 @@ impl Border {
Self::create("komorebi-border-window")?;
}
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;
}
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let title = &window.title()?;
let exe_name = &window.exe()?;
let class = &window.class()?;
let should_expand_border = should_act(
title,
exe_name,
class,
&border_overflows,
&regex_identifiers,
);
if should_expand_border {
rect.left -= invisible_borders.left;

View File

@@ -39,7 +39,7 @@ impl Hidden {
let class_name = PCSTR(name.as_ptr());
let brush = WindowsApi::create_solid_brush(TRANSPARENCY_COLOUR);
let window_class = WNDCLASSA {
hInstance: instance,
hInstance: instance.into(),
lpszClassName: class_name,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(windows_callbacks::hidden_window),

View File

@@ -1,5 +1,10 @@
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
#![allow(clippy::missing_errors_doc, clippy::redundant_pub_crate)]
#![allow(
clippy::missing_errors_doc,
clippy::redundant_pub_crate,
clippy::significant_drop_tightening,
clippy::significant_drop_in_scrutinee
)]
use std::collections::HashMap;
use std::fs::File;
@@ -27,6 +32,7 @@ use os_info::Version;
#[cfg(feature = "deadlock_detection")]
use parking_lot::deadlock;
use parking_lot::Mutex;
use regex::Regex;
use schemars::JsonSchema;
use serde::Serialize;
use sysinfo::Process;
@@ -40,6 +46,9 @@ use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey;
use crate::hidden::Hidden;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
use komorebi_core::SocketMessage;
@@ -81,36 +90,83 @@ type WorkspaceRule = (usize, usize, bool);
lazy_static! {
static ref HIDDEN_HWNDS: Arc<Mutex<Vec<isize>>> = Arc::new(Mutex::new(vec![]));
static ref LAYERED_WHITELIST: Arc<Mutex<Vec<String>>> =
Arc::new(Mutex::new(vec!["steam.exe".to_string()]));
static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<String>>> =
static ref LAYERED_WHITELIST: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
]));
static ref TRAY_AND_MULTI_WINDOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> =
Arc::new(Mutex::new(vec![
"explorer.exe".to_string(),
"firefox.exe".to_string(),
"chrome.exe".to_string(),
"idea64.exe".to_string(),
"ApplicationFrameHost.exe".to_string(),
"steam.exe".to_string(),
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("explorer.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("chrome.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("ApplicationFrameHost.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("steam.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}
]));
static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"firefox.exe".to_string(),
"idea64.exe".to_string(),
static ref OBJECT_NAME_CHANGE_ON_LAUNCH: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("firefox.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: String::from("idea64.exe"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
]));
static ref MONITOR_INDEX_PREFERENCES: Arc<Mutex<HashMap<usize, Rect>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, WorkspaceRule>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![]));
static ref FLOAT_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![
// mstsc.exe creates these on Windows 11 when a WSL process is launched
// https://github.com/LGUG2Z/komorebi/issues/74
"OPContainerClass".to_string(),
"IHWindowClass".to_string()
IdWithIdentifier {
kind: ApplicationIdentifier::Class,
id: String::from("OPContainerClass"),
matching_strategy: Option::from(MatchingStrategy::Equals),
},
IdWithIdentifier {
kind: ApplicationIdentifier::Class,
id: String::from("IHWindowClass"),
matching_strategy: Option::from(MatchingStrategy::Equals),
}
]));
static ref PERMAIGNORE_CLASSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"Chrome_RenderWidgetHostHWND".to_string(),
]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<IdWithIdentifier>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(),
"vcxsrv.exe".to_string(),
@@ -313,7 +369,7 @@ pub fn current_virtual_desktop() -> Option<Vec<u8>> {
// This is the path on Windows 11
if current.is_none() {
current = hkcu
.open_subkey(r#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops"#)
.open_subkey(r"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops")
.ok()
.and_then(
|desktops| match desktops.get_raw_value("CurrentVirtualDesktop") {
@@ -352,7 +408,7 @@ pub struct Notification {
pub fn notify_subscribers(notification: &str) -> Result<()> {
let mut stale_subscriptions = vec![];
let mut subscriptions = SUBSCRIPTION_PIPES.lock();
for (subscriber, pipe) in subscriptions.iter_mut() {
for (subscriber, pipe) in &mut *subscriptions {
match writeln!(pipe, "{notification}") {
Ok(_) => {
tracing::debug!("pushed notification to subscriber: {}", subscriber);

View File

@@ -20,6 +20,8 @@ use parking_lot::Mutex;
use schemars::schema_for;
use uds_windows::UnixStream;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis;
use komorebi_core::FocusFollowsMouseImplementation;
@@ -238,16 +240,40 @@ impl WindowManager {
self.handle_definitive_workspace_rules(id, monitor_idx, workspace_idx)?;
}
}
SocketMessage::ManageRule(_, ref id) => {
SocketMessage::ManageRule(identifier, ref id) => {
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
if !manage_identifiers.contains(id) {
manage_identifiers.push(id.to_string());
let mut should_push = true;
for m in &*manage_identifiers {
if m.id.eq(id) {
should_push = false;
}
}
if should_push {
manage_identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
}
SocketMessage::FloatRule(identifier, ref id) => {
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
if !float_identifiers.contains(id) {
float_identifiers.push(id.to_string());
let mut should_push = true;
for f in &*float_identifiers {
if f.id.eq(id) {
should_push = false;
}
}
if should_push {
float_identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
let invisible_borders = self.invisible_borders;
@@ -259,9 +285,8 @@ impl WindowManager {
.focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace"))?
.containers()
.iter()
{
for window in container.windows().iter() {
for window in container.windows() {
match identifier {
ApplicationIdentifier::Exe => {
if window.exe()? == *id {
@@ -423,6 +448,7 @@ impl WindowManager {
SocketMessage::Retile => self.retile_all(false)?,
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
SocketMessage::CycleLayout(direction) => self.cycle_layout(direction)?,
SocketMessage::ChangeLayoutCustom(ref path) => {
self.change_workspace_custom_layout(path.clone())?;
}
@@ -891,28 +917,75 @@ impl WindowManager {
SocketMessage::WatchConfiguration(enable) => {
self.watch_configuration(enable)?;
}
SocketMessage::IdentifyBorderOverflowApplication(_, ref id) => {
SocketMessage::IdentifyBorderOverflowApplication(identifier, ref id) => {
let mut identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
let mut should_push = true;
for i in &*identifiers {
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
}
SocketMessage::IdentifyObjectNameChangeApplication(_, ref id) => {
SocketMessage::IdentifyObjectNameChangeApplication(identifier, ref id) => {
let mut identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
let mut should_push = true;
for i in &*identifiers {
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
}
SocketMessage::IdentifyTrayApplication(_, ref id) => {
SocketMessage::IdentifyTrayApplication(identifier, ref id) => {
let mut identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
let mut should_push = true;
for i in &*identifiers {
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
}
SocketMessage::IdentifyLayeredApplication(_, ref id) => {
SocketMessage::IdentifyLayeredApplication(identifier, ref id) => {
let mut identifiers = LAYERED_WHITELIST.lock();
if !identifiers.contains(id) {
identifiers.push(id.to_string());
let mut should_push = true;
for i in &*identifiers {
if i.id.eq(id) {
should_push = false;
}
}
if should_push {
identifiers.push(IdWithIdentifier {
kind: identifier,
id: id.clone(),
matching_strategy: Option::from(MatchingStrategy::Legacy),
});
}
}
SocketMessage::ManageFocusedWindow => {
@@ -1182,6 +1255,7 @@ impl WindowManager {
match message {
SocketMessage::ChangeLayout(_)
| SocketMessage::CycleLayout(_)
| SocketMessage::ChangeLayoutCustom(_)
| SocketMessage::FlipLayout(_)
| SocketMessage::ManageFocusedWindow
@@ -1341,7 +1415,8 @@ pub fn read_commands_tcp(
break;
}
Ok(size) => {
let Ok(message) = SocketMessage::from_str(&String::from_utf8_lossy(&buf[..size])) else {
let Ok(message) = SocketMessage::from_str(&String::from_utf8_lossy(&buf[..size]))
else {
tracing::warn!("client sent an invalid message, disconnecting: {addr}");
let mut connections = TCP_CONNECTIONS.lock();
connections.remove(addr);

View File

@@ -15,6 +15,7 @@ use komorebi_core::WindowContainerBehaviour;
use crate::border::Border;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::window::should_act;
use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
@@ -29,6 +30,7 @@ use crate::BORDER_HIDDEN;
use crate::BORDER_HWND;
use crate::DATA_DIR;
use crate::HIDDEN_HWNDS;
use crate::REGEX_IDENTIFIERS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
#[tracing::instrument]
@@ -179,15 +181,26 @@ impl WindowManager {
{
let tray_and_multi_window_identifiers =
TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let title = &window.title()?;
let exe_name = &window.exe()?;
let class = &window.class()?;
// We don't want to purge windows that have been deliberately hidden by us, eg. when
// they are not on the top of a container stack.
let programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
let should_act = should_act(
title,
exe_name,
class,
&tray_and_multi_window_identifiers,
&regex_identifiers,
);
if ((!window.is_window()
|| tray_and_multi_window_identifiers.contains(&window.exe()?))
|| tray_and_multi_window_identifiers.contains(&window.class()?))
&& !programmatically_hidden_hwnds.contains(&window.hwnd)
if !window.is_window()
|| should_act
|| !programmatically_hidden_hwnds.contains(&window.hwnd)
{
hide = true;
}

View File

@@ -22,7 +22,9 @@ use crate::FLOAT_IDENTIFIERS;
use crate::HIDING_BEHAVIOUR;
use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REGEX_IDENTIFIERS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
use color_eyre::Result;
@@ -33,6 +35,7 @@ use hotwatch::Hotwatch;
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
use komorebi_core::config_generation::ApplicationOptions;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingStrategy;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::DefaultLayout;
use komorebi_core::FocusFollowsMouseImplementation;
@@ -44,6 +47,7 @@ use komorebi_core::Rect;
use komorebi_core::SocketMessage;
use komorebi_core::WindowContainerBehaviour;
use parking_lot::Mutex;
use regex::Regex;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
@@ -67,6 +71,16 @@ pub struct Rgb {
pub b: u32,
}
impl From<u32> for Rgb {
fn from(value: u32) -> Self {
Self {
r: value & 0xff,
g: value >> 8 & 0xff,
b: value >> 16 & 0xff,
}
}
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct ActiveWindowBorderColours {
/// Border colour when the container contains a single window
@@ -128,6 +142,7 @@ impl From<&Workspace> for WorkspaceConfig {
let rule = IdWithIdentifier {
kind: ApplicationIdentifier::Exe,
id: identifier.clone(),
matching_strategy: None,
};
if *is_initial {
@@ -239,12 +254,18 @@ pub struct StaticConfig {
/// Path to applications.yaml from komorebi-application-specific-configurations (default: None)
#[serde(skip_serializing_if = "Option::is_none")]
pub app_specific_configuration_path: Option<PathBuf>,
/// Width of the active window border (default: 20)
/// DEPRECATED from v0.1.19: use active_window_border_width instead
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<i32>,
/// Offset of the active window border (default: None)
/// DEPRECATED from v0.1.19: use active_window_border_offset instead
#[serde(skip_serializing_if = "Option::is_none")]
pub border_offset: Option<Rect>,
/// Width of the active window border (default: 20)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_width: Option<i32>,
/// Offset of the active window border (default: None)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border_offset: Option<i32>,
/// Display an active window border (default: false)
#[serde(skip_serializing_if = "Option::is_none")]
pub active_window_border: Option<bool>,
@@ -287,9 +308,13 @@ pub struct StaticConfig {
/// Identify applications that send EVENT_OBJECT_NAMECHANGE on launch (very rare)
#[serde(skip_serializing_if = "Option::is_none")]
pub object_name_change_applications: Option<Vec<IdWithIdentifier>>,
/// Set monitor index preferences
#[serde(skip_serializing_if = "Option::is_none")]
pub monitor_index_preferences: Option<HashMap<usize, Rect>>,
}
impl From<&WindowManager> for StaticConfig {
#[allow(clippy::too_many_lines)]
fn from(value: &WindowManager) -> Self {
let default_invisible_borders = Rect {
left: 7,
@@ -348,6 +373,24 @@ impl From<&WindowManager> for StaticConfig {
}
}
let border_colours = if BORDER_COLOUR_SINGLE.load(Ordering::SeqCst) == 0 {
None
} else {
Option::from(ActiveWindowBorderColours {
single: Rgb::from(BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)),
stack: Rgb::from(if BORDER_COLOUR_STACK.load(Ordering::SeqCst) == 0 {
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
} else {
BORDER_COLOUR_STACK.load(Ordering::SeqCst)
}),
monocle: Rgb::from(if BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst) == 0 {
BORDER_COLOUR_SINGLE.load(Ordering::SeqCst)
} else {
BORDER_COLOUR_MONOCLE.load(Ordering::SeqCst)
}),
})
};
Self {
invisible_borders: if value.invisible_borders == default_invisible_borders {
None
@@ -363,10 +406,14 @@ impl From<&WindowManager> for StaticConfig {
focus_follows_mouse: value.focus_follows_mouse,
mouse_follows_focus: Option::from(value.mouse_follows_focus),
app_specific_configuration_path: None,
border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
border_offset: *BORDER_OFFSET.lock(),
active_window_border_width: Option::from(BORDER_WIDTH.load(Ordering::SeqCst)),
active_window_border_offset: BORDER_OFFSET
.lock()
.map_or(None, |offset| Option::from(offset.left)),
border_width: None,
border_offset: None,
active_window_border: Option::from(BORDER_ENABLED.load(Ordering::SeqCst)),
active_window_border_colours: None,
active_window_border_colours: border_colours,
default_workspace_padding: Option::from(
DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst),
),
@@ -383,13 +430,19 @@ impl From<&WindowManager> for StaticConfig {
tray_and_multi_window_applications: None,
layered_applications: None,
object_name_change_applications: None,
monitor_index_preferences: Option::from(MONITOR_INDEX_PREFERENCES.lock().clone()),
}
}
}
impl StaticConfig {
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
fn apply_globals(&self) -> Result<()> {
fn apply_globals(&mut self) -> Result<()> {
if let Some(monitor_index_preferences) = &self.monitor_index_preferences {
let mut preferences = MONITOR_INDEX_PREFERENCES.lock();
*preferences = monitor_index_preferences.clone();
}
if let Some(behaviour) = self.window_hiding_behaviour {
let mut window_hiding_behaviour = HIDING_BEHAVIOUR.lock();
*window_hiding_behaviour = behaviour;
@@ -407,14 +460,31 @@ impl StaticConfig {
DEFAULT_WORKSPACE_PADDING.store(workspace, Ordering::SeqCst);
}
if let Some(width) = self.border_width {
BORDER_WIDTH.store(width, Ordering::SeqCst);
}
self.active_window_border_width.map_or_else(
|| {
BORDER_WIDTH.store(20, Ordering::SeqCst);
},
|width| {
BORDER_WIDTH.store(width, Ordering::SeqCst);
},
);
self.active_window_border_offset.map_or_else(
|| {
let mut border_offset = BORDER_OFFSET.lock();
*border_offset = None;
},
|offset| {
let new_border_offset = Rect {
left: offset,
top: offset,
right: offset * 2,
bottom: offset * 2,
};
if let Some(offset) = self.border_offset {
let mut border_offset = BORDER_OFFSET.lock();
*border_offset = Some(offset);
}
let mut border_offset = BORDER_OFFSET.lock();
*border_offset = Some(new_border_offset);
},
);
if let Some(colours) = &self.active_window_border_colours {
BORDER_COLOUR_SINGLE.store(
@@ -436,56 +506,111 @@ impl StaticConfig {
}
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
let mut tray_and_multi_window_identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
let mut border_overflow_identifiers = BORDER_OVERFLOW_IDENTIFIERS.lock();
let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
let mut layered_identifiers = LAYERED_WHITELIST.lock();
if let Some(float) = &self.float_rules {
if let Some(float) = &mut self.float_rules {
for identifier in float {
if !float_identifiers.contains(&identifier.id) {
float_identifiers.push(identifier.id.clone());
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !float_identifiers.contains(identifier) {
float_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(float) = &self.manage_rules {
for identifier in float {
if !manage_identifiers.contains(&identifier.id) {
manage_identifiers.push(identifier.id.clone());
if let Some(manage) = &mut self.manage_rules {
for identifier in manage {
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !manage_identifiers.contains(identifier) {
manage_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &self.object_name_change_applications {
if let Some(identifiers) = &mut self.object_name_change_applications {
for identifier in identifiers {
if !object_name_change_identifiers.contains(&identifier.id) {
object_name_change_identifiers.push(identifier.id.clone());
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !object_name_change_identifiers.contains(identifier) {
object_name_change_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &self.layered_applications {
if let Some(identifiers) = &mut self.layered_applications {
for identifier in identifiers {
if !layered_identifiers.contains(&identifier.id) {
layered_identifiers.push(identifier.id.clone());
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !layered_identifiers.contains(identifier) {
layered_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &self.border_overflow_applications {
if let Some(identifiers) = &mut self.border_overflow_applications {
for identifier in identifiers {
if !border_overflow_identifiers.contains(&identifier.id) {
border_overflow_identifiers.push(identifier.id.clone());
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !border_overflow_identifiers.contains(identifier) {
border_overflow_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
if let Some(identifiers) = &self.tray_and_multi_window_applications {
if let Some(identifiers) = &mut self.tray_and_multi_window_applications {
for identifier in identifiers {
if !tray_and_multi_window_identifiers.contains(&identifier.id) {
tray_and_multi_window_identifiers.push(identifier.id.clone());
if identifier.matching_strategy.is_none() {
identifier.matching_strategy = Option::from(MatchingStrategy::Legacy);
}
if !tray_and_multi_window_identifiers.contains(identifier) {
tray_and_multi_window_identifiers.push(identifier.clone());
if matches!(identifier.matching_strategy, Some(MatchingStrategy::Regex)) {
let re = Regex::new(&identifier.id)?;
regex_identifiers.insert(identifier.id.clone(), re);
}
}
}
}
@@ -500,11 +625,25 @@ impl StaticConfig {
let content = std::fs::read_to_string(stringified)?;
let asc = ApplicationConfigurationGenerator::load(&content)?;
for entry in asc {
for mut entry in asc {
if let Some(float) = entry.float_identifiers {
for f in float {
if !float_identifiers.contains(&f.id) {
float_identifiers.push(f.id.clone());
let mut without_comment: IdWithIdentifier = f.into();
if without_comment.matching_strategy.is_none() {
without_comment.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !float_identifiers.contains(&without_comment) {
float_identifiers.push(without_comment.clone());
if matches!(
without_comment.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&without_comment.id)?;
regex_identifiers.insert(without_comment.id.clone(), re);
}
}
}
}
@@ -512,31 +651,94 @@ impl StaticConfig {
for o in options {
match o {
ApplicationOptions::ObjectNameChange => {
if !object_name_change_identifiers.contains(&entry.identifier.id) {
object_name_change_identifiers
.push(entry.identifier.id.clone());
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !object_name_change_identifiers.contains(&entry.identifier) {
object_name_change_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::Layered => {
if !layered_identifiers.contains(&entry.identifier.id) {
layered_identifiers.push(entry.identifier.id.clone());
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !layered_identifiers.contains(&entry.identifier) {
layered_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::BorderOverflow => {
if !border_overflow_identifiers.contains(&entry.identifier.id) {
border_overflow_identifiers.push(entry.identifier.id.clone());
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !border_overflow_identifiers.contains(&entry.identifier) {
border_overflow_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::TrayAndMultiWindow => {
if !tray_and_multi_window_identifiers.contains(&entry.identifier.id)
{
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !tray_and_multi_window_identifiers.contains(&entry.identifier) {
tray_and_multi_window_identifiers
.push(entry.identifier.id.clone());
.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
ApplicationOptions::Force => {
if !manage_identifiers.contains(&entry.identifier.id) {
manage_identifiers.push(entry.identifier.id.clone());
if entry.identifier.matching_strategy.is_none() {
entry.identifier.matching_strategy =
Option::from(MatchingStrategy::Legacy);
}
if !manage_identifiers.contains(&entry.identifier) {
manage_identifiers.push(entry.identifier.clone());
if matches!(
entry.identifier.matching_strategy,
Some(MatchingStrategy::Regex)
) {
let re = Regex::new(&entry.identifier.id)?;
regex_identifiers.insert(entry.identifier.id.clone(), re);
}
}
}
}
@@ -554,7 +756,7 @@ impl StaticConfig {
incoming: Arc<Mutex<Receiver<WindowManagerEvent>>>,
) -> Result<WindowManager> {
let content = std::fs::read_to_string(path)?;
let value: Self = serde_json::from_str(&content)?;
let mut value: Self = serde_json::from_str(&content)?;
value.apply_globals()?;
let socket = DATA_DIR.join("komorebi.sock");
@@ -674,7 +876,7 @@ impl StaticConfig {
pub fn reload(path: &PathBuf, wm: &mut WindowManager) -> Result<()> {
let content = std::fs::read_to_string(path)?;
let value: Self = serde_json::from_str(&content)?;
let mut value: Self = serde_json::from_str(&content)?;
value.apply_globals()?;

View File

@@ -1,4 +1,5 @@
use crate::com::SetCloak;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fmt::Display;
use std::fmt::Formatter;
@@ -7,6 +8,9 @@ use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::config_generation::MatchingStrategy;
use regex::Regex;
use schemars::JsonSchema;
use serde::ser::Error;
use serde::ser::SerializeStruct;
@@ -17,6 +21,7 @@ use winput::press;
use winput::release;
use winput::Vk;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
@@ -33,6 +38,7 @@ use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::NO_TITLEBAR;
use crate::PERMAIGNORE_CLASSES;
use crate::REGEX_IDENTIFIERS;
use crate::WSL2_UI_PROCESSES;
#[derive(Debug, Clone, Copy, JsonSchema)]
@@ -124,15 +130,21 @@ impl Window {
top: bool,
) -> Result<()> {
let mut rect = *layout;
let mut should_remove_border = true;
let border_overflows = BORDER_OVERFLOW_IDENTIFIERS.lock();
if border_overflows.contains(&self.title()?)
|| border_overflows.contains(&self.exe()?)
|| border_overflows.contains(&self.class()?)
{
should_remove_border = false;
}
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let title = &self.title()?;
let class = &self.class()?;
let exe_name = &self.exe()?;
let should_remove_border = !should_act(
title,
exe_name,
class,
&border_overflows,
&regex_identifiers,
);
if should_remove_border {
// Remove the invisible borders
@@ -348,8 +360,7 @@ impl Window {
let mut ex_style = self.ex_style()?;
ex_style.insert(ExtendedWindowStyle::LAYERED);
self.update_ex_style(&ex_style)?;
WindowsApi::set_transparent(self.hwnd());
Ok(())
WindowsApi::set_transparent(self.hwnd())
}
pub fn opaque(self) -> Result<()> {
@@ -470,42 +481,38 @@ fn window_is_eligible(
}
}
let mut should_float = false;
let regex_identifiers = REGEX_IDENTIFIERS.lock();
{
let float_identifiers = FLOAT_IDENTIFIERS.lock();
for identifier in float_identifiers.iter() {
if title.starts_with(identifier) || title.ends_with(identifier) {
should_float = true;
}
let float_identifiers = FLOAT_IDENTIFIERS.lock();
let should_float = should_act(
title,
exe_name,
class,
&float_identifiers,
&regex_identifiers,
);
if class.starts_with(identifier) || class.ends_with(identifier) {
should_float = true;
}
if identifier == exe_name {
should_float = true;
}
}
};
let managed_override = {
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
manage_identifiers.contains(exe_name)
|| manage_identifiers.contains(class)
|| manage_identifiers.contains(title)
};
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
let managed_override = should_act(
title,
exe_name,
class,
&manage_identifiers,
&regex_identifiers,
);
if should_float && !managed_override {
return false;
}
let allow_layered = {
let layered_whitelist = LAYERED_WHITELIST.lock();
layered_whitelist.contains(exe_name)
|| layered_whitelist.contains(class)
|| layered_whitelist.contains(title)
};
let layered_whitelist = LAYERED_WHITELIST.lock();
let allow_layered = should_act(
title,
exe_name,
class,
&layered_whitelist,
&regex_identifiers,
);
// TODO: might need this for transparency
// let allow_layered = true;
@@ -536,3 +543,131 @@ fn window_is_eligible(
false
}
#[allow(clippy::cognitive_complexity, clippy::too_many_lines)]
pub fn should_act(
title: &str,
exe_name: &str,
class: &str,
identifiers: &[IdWithIdentifier],
regex_identifiers: &HashMap<String, Regex>,
) -> bool {
let mut should_act = false;
for identifier in identifiers {
match identifier.matching_strategy {
None => {
panic!("there is no matching strategy identified for this rule");
}
Some(MatchingStrategy::Legacy) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) || title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) || class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Equals) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.eq(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.eq(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::StartsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.starts_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.starts_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::EndsWith) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.ends_with(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.ends_with(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Contains) => match identifier.kind {
ApplicationIdentifier::Title => {
if title.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Class => {
if class.contains(&identifier.id) {
should_act = true;
}
}
ApplicationIdentifier::Exe => {
if exe_name.contains(&identifier.id) {
should_act = true;
}
}
},
Some(MatchingStrategy::Regex) => match identifier.kind {
ApplicationIdentifier::Title => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(title) {
should_act = true;
}
}
}
ApplicationIdentifier::Class => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(class) {
should_act = true;
}
}
}
ApplicationIdentifier::Exe => {
if let Some(re) = regex_identifiers.get(&identifier.id) {
if re.is_match(exe_name) {
should_act = true;
}
}
}
},
}
}
should_act
}

View File

@@ -17,6 +17,7 @@ use schemars::JsonSchema;
use serde::Serialize;
use uds_windows::UnixListener;
use komorebi_core::config_generation::IdWithIdentifier;
use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement;
use komorebi_core::Axis;
@@ -50,6 +51,7 @@ use crate::FLOAT_IDENTIFIERS;
use crate::HOME_DIR;
use crate::LAYERED_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::MONITOR_INDEX_PREFERENCES;
use crate::NO_TITLEBAR;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REMOVE_TITLEBARS;
@@ -92,12 +94,13 @@ pub struct State {
pub mouse_follows_focus: bool,
pub has_pending_raise_op: bool,
pub remove_titlebars: bool,
pub float_identifiers: Vec<String>,
pub manage_identifiers: Vec<String>,
pub layered_whitelist: Vec<String>,
pub tray_and_multi_window_identifiers: Vec<String>,
pub border_overflow_identifiers: Vec<String>,
pub name_change_on_launch_identifiers: Vec<String>,
pub float_identifiers: Vec<IdWithIdentifier>,
pub manage_identifiers: Vec<IdWithIdentifier>,
pub layered_whitelist: Vec<IdWithIdentifier>,
pub tray_and_multi_window_identifiers: Vec<IdWithIdentifier>,
pub border_overflow_identifiers: Vec<IdWithIdentifier>,
pub name_change_on_launch_identifiers: Vec<IdWithIdentifier>,
pub monitor_index_preferences: HashMap<usize, Rect>,
}
impl AsRef<Self> for WindowManager {
@@ -126,6 +129,7 @@ impl From<&WindowManager> for State {
tray_and_multi_window_identifiers: TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock().clone(),
border_overflow_identifiers: BORDER_OVERFLOW_IDENTIFIERS.lock().clone(),
name_change_on_launch_identifiers: OBJECT_NAME_CHANGE_ON_LAUNCH.lock().clone(),
monitor_index_preferences: MONITOR_INDEX_PREFERENCES.lock().clone(),
}
}
}
@@ -1701,6 +1705,29 @@ impl WindowManager {
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn cycle_layout(&mut self, direction: CycleDirection) -> Result<()> {
tracing::info!("cycling layout");
let workspace = self.focused_workspace_mut()?;
let current_layout = workspace.layout();
match current_layout {
Layout::Default(current) => {
let new_layout = match direction {
CycleDirection::Previous => current.cycle_previous(),
CycleDirection::Next => current.cycle_next(),
};
tracing::info!("next layout: {new_layout}");
workspace.set_layout(Layout::Default(new_layout));
}
Layout::Custom(_) => {}
}
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn change_workspace_custom_layout(&mut self, path: PathBuf) -> Result<()> {
tracing::info!("changing layout");

View File

@@ -4,9 +4,11 @@ use std::fmt::Formatter;
use schemars::JsonSchema;
use serde::Serialize;
use crate::window::should_act;
use crate::window::Window;
use crate::winevent::WinEvent;
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REGEX_IDENTIFIERS;
#[derive(Debug, Copy, Clone, Serialize, JsonSchema)]
#[serde(tag = "type", content = "content")]
@@ -131,11 +133,21 @@ impl WindowManagerEvent {
// [yatta\src\windows_event.rs:110] event = 32779 ObjectLocationChange
let object_name_change_on_launch = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
let regex_identifiers = REGEX_IDENTIFIERS.lock();
if object_name_change_on_launch.contains(&window.exe().ok()?)
|| object_name_change_on_launch.contains(&window.class().ok()?)
|| object_name_change_on_launch.contains(&window.title().ok()?)
{
let title = &window.title().ok()?;
let exe_name = &window.exe().ok()?;
let class = &window.class().ok()?;
let should_trigger = should_act(
title,
exe_name,
class,
&object_name_change_on_launch,
&regex_identifiers,
);
if should_trigger {
Option::from(Self::Show(winevent, window))
} else {
None

View File

@@ -234,9 +234,7 @@ impl WindowsApi {
}
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> Result<()> {
unsafe { EnumWindows(callback, LPARAM(callback_data_address)) }
.ok()
.process()
unsafe { EnumWindows(callback, LPARAM(callback_data_address)) }.process()
}
pub fn load_workspace_information(monitors: &mut Ring<Monitor>) -> Result<()> {
@@ -275,9 +273,7 @@ impl WindowsApi {
}
pub fn allow_set_foreground_window(process_id: u32) -> Result<()> {
unsafe { AllowSetForegroundWindow(process_id) }
.ok()
.process()
unsafe { AllowSetForegroundWindow(process_id) }.process()
}
pub fn monitor_from_window(hwnd: HWND) -> isize {
@@ -313,7 +309,7 @@ impl WindowsApi {
}
pub fn bring_window_to_top(hwnd: HWND) -> Result<()> {
unsafe { BringWindowToTop(hwnd) }.ok().process()
unsafe { BringWindowToTop(hwnd) }.process()
}
pub fn raise_window(hwnd: HWND) -> Result<()> {
@@ -353,7 +349,6 @@ impl WindowsApi {
SET_WINDOW_POS_FLAGS(flags),
)
}
.ok()
.process()
}
@@ -368,9 +363,7 @@ impl WindowsApi {
}
fn post_message(hwnd: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> Result<()> {
unsafe { PostMessageW(hwnd, message, wparam, lparam) }
.ok()
.process()
unsafe { PostMessageW(hwnd, message, wparam, lparam) }.process()
}
pub fn close_window(hwnd: HWND) -> Result<()> {
@@ -436,18 +429,18 @@ impl WindowsApi {
pub fn window_rect(hwnd: HWND) -> Result<Rect> {
let mut rect = unsafe { std::mem::zeroed() };
unsafe { GetWindowRect(hwnd, &mut rect) }.ok().process()?;
unsafe { GetWindowRect(hwnd, &mut rect) }.process()?;
Ok(Rect::from(rect))
}
fn set_cursor_pos(x: i32, y: i32) -> Result<()> {
unsafe { SetCursorPos(x, y) }.ok().process()
unsafe { SetCursorPos(x, y) }.process()
}
pub fn cursor_pos() -> Result<POINT> {
let mut cursor_pos = POINT::default();
unsafe { GetCursorPos(&mut cursor_pos) }.ok().process()?;
unsafe { GetCursorPos(&mut cursor_pos) }.process()?;
Ok(cursor_pos)
}
@@ -489,7 +482,7 @@ impl WindowsApi {
let mut session_id = 0;
unsafe {
if ProcessIdToSessionId(process_id, &mut session_id).as_bool() {
if ProcessIdToSessionId(process_id, &mut session_id).is_ok() {
Ok(session_id)
} else {
Err(anyhow!("could not determine current session id"))
@@ -565,7 +558,7 @@ impl WindowsApi {
}
pub fn close_process(handle: HANDLE) -> Result<()> {
unsafe { CloseHandle(handle) }.ok().process()
unsafe { CloseHandle(handle) }.process()
}
pub fn process_handle(process_id: u32) -> Result<HANDLE> {
@@ -580,7 +573,6 @@ impl WindowsApi {
unsafe {
QueryFullProcessImageNameW(handle, PROCESS_NAME_WIN32, PWSTR(text_ptr), &mut len)
}
.ok()
.process()?;
Ok(String::from_utf16(&path[..len as usize])?)
@@ -673,7 +665,6 @@ impl WindowsApi {
pub fn set_process_dpi_awareness_context() -> Result<()> {
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }
.ok()
.process()
}
@@ -685,7 +676,6 @@ impl WindowsApi {
update_flags: SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS,
) -> Result<()> {
unsafe { SystemParametersInfoW(action, ui_param, Option::from(pv_param), update_flags) }
.ok()
.process()
}
@@ -777,19 +767,21 @@ impl WindowsApi {
None,
);
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY);
SetLayeredWindowAttributes(hwnd, COLORREF(TRANSPARENCY_COLOUR), 0, LWA_COLORKEY)?;
hwnd
}
.process()
}
pub fn set_transparent(hwnd: HWND) {
pub fn set_transparent(hwnd: HWND) -> Result<()> {
unsafe {
#[allow(clippy::cast_sign_loss)]
// TODO: alpha should be configurable
SetLayeredWindowAttributes(hwnd, COLORREF(-1i32 as u32), 150, LWA_ALPHA);
SetLayeredWindowAttributes(hwnd, COLORREF(-1i32 as u32), 150, LWA_ALPHA)?;
}
Ok(())
}
pub fn create_hidden_window(name: PCSTR, instance: HMODULE) -> Result<isize> {

View File

@@ -104,11 +104,17 @@ impl Workspace {
if let Some(layout) = &config.layout {
self.layout = Layout::Default(*layout);
self.tile = true;
}
if let Some(pathbuf) = &config.custom_layout {
let layout = CustomLayout::from_path_buf(pathbuf.clone())?;
self.layout = Layout::Custom(layout);
self.tile = true;
}
if config.custom_layout.is_none() && config.layout.is_none() {
self.tile = false;
}
if let Some(layout_rules) = &config.layout_rules {
@@ -775,6 +781,16 @@ impl Workspace {
}
fn enforce_resize_constraints(&mut self) {
match self.layout {
Layout::Default(DefaultLayout::BSP) => self.enforce_resize_constraints_for_bsp(),
Layout::Default(DefaultLayout::UltrawideVerticalStack) => {
self.enforce_resize_for_ultrawide();
}
_ => self.enforce_no_resize(),
}
}
fn enforce_resize_constraints_for_bsp(&mut self) {
for (i, rect) in self.resize_dimensions_mut().iter_mut().enumerate() {
if let Some(rect) = rect {
// Even containers can't be resized to the bottom
@@ -800,6 +816,72 @@ impl Workspace {
}
}
fn enforce_resize_for_ultrawide(&mut self) {
let resize_dimensions = self.resize_dimensions_mut();
match resize_dimensions.len() {
// Single window can not be resized at all
0 | 1 => self.enforce_no_resize(),
// Two windows can only be resized in the middle
2 => {
// Zero is actually on the right
if let Some(mut right) = resize_dimensions[0] {
right.top = 0;
right.bottom = 0;
right.right = 0;
}
// One is on the left
if let Some(mut left) = resize_dimensions[1] {
left.top = 0;
left.bottom = 0;
left.left = 0;
}
}
// Three or more windows means 0 is in center, 1 is at the left, 2.. are a vertical
// stack on the right
_ => {
// Central can be resized left or right
if let Some(mut right) = resize_dimensions[0] {
right.top = 0;
right.bottom = 0;
}
// Left one can only be resized to the right
if let Some(mut left) = resize_dimensions[1] {
left.top = 0;
left.bottom = 0;
left.left = 0;
}
// Handle stack on the right
let stack_size = resize_dimensions[2..].len();
for (i, rect) in resize_dimensions[2..].iter_mut().enumerate() {
if let Some(rect) = rect {
// No containers can resize to the right
rect.right = 0;
// First container in stack cant resize up
if i == 0 {
rect.top = 0;
} else if i == stack_size - 1 {
// Last cant be resized to the bottom
rect.bottom = 0;
}
}
}
}
}
}
fn enforce_no_resize(&mut self) {
for rect in self.resize_dimensions_mut().iter_mut().flatten() {
rect.left = 0;
rect.right = 0;
rect.top = 0;
rect.bottom = 0;
}
}
pub fn new_monocle_container(&mut self) -> Result<()> {
let focused_idx = self.focused_container_idx();
let container = self

View File

@@ -196,6 +196,10 @@ ChangeLayout(default_layout) {
RunWait("komorebic.exe change-layout " default_layout, , "Hide")
}
CycleLayout(operation_direction) {
RunWait("komorebic.exe cycle-layout " operation_direction, , "Hide")
}
LoadCustomLayout(path) {
RunWait("komorebic.exe load-custom-layout " path, , "Hide")
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.17"
version = "0.1.19"
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"]
@@ -28,5 +28,5 @@ serde_json = "1"
serde_yaml = "0.9"
sysinfo = "0.29"
uds_windows = "1"
which = "4"
which = "5"
windows = { workspace = true }

View File

@@ -125,6 +125,7 @@ gen_enum_subcommand_args! {
CycleStack: CycleDirection,
FlipLayout: Axis,
ChangeLayout: DefaultLayout,
CycleLayout: CycleDirection,
WatchConfiguration: BooleanState,
MouseFollowsFocus: BooleanState,
Query: StateQuery,
@@ -607,6 +608,7 @@ struct ActiveWindowBorderOffset {
}
#[derive(Parser, AhkFunction)]
#[allow(clippy::struct_excessive_bools)]
struct Start {
/// Allow the use of komorebi's custom focus-follows-mouse implementation
#[clap(action, short, long = "ffm")]
@@ -623,6 +625,16 @@ struct Start {
/// Start whkd in a background process
#[clap(action, long)]
whkd: bool,
/// Start autohotkey configuration file
#[clap(action, long)]
ahk: bool,
}
#[derive(Parser, AhkFunction)]
struct Stop {
/// Stop whkd if it is running as a background process
#[clap(action, long)]
whkd: bool,
}
#[derive(Parser, AhkFunction)]
@@ -683,6 +695,19 @@ struct AltFocusHack {
boolean_state: BooleanState,
}
#[derive(Parser, AhkFunction)]
struct EnableAutostart {
/// Path to a static configuration JSON file
#[clap(action, short, long)]
config: String,
/// Enable autostart of whkd
#[clap(action, long)]
whkd: bool,
/// Enable autostart of ahk
#[clap(action, long)]
ahk: bool,
}
#[derive(Parser)]
#[clap(author, about, version)]
struct Opts {
@@ -695,7 +720,7 @@ enum SubCommand {
/// Start komorebi.exe as a background process
Start(Start),
/// Stop the komorebi.exe process and restore all hidden windows
Stop,
Stop(Stop),
/// Output various important komorebi-related environment values
Check,
/// Show a JSON representation of the current window manager state
@@ -841,6 +866,9 @@ enum SubCommand {
/// Set the layout on the focused workspace
#[clap(arg_required_else_help = true)]
ChangeLayout(ChangeLayout),
/// Cycle between available layouts
#[clap(arg_required_else_help = true)]
CycleLayout(CycleLayout),
/// Load a custom layout from file for the focused workspace
#[clap(arg_required_else_help = true)]
LoadCustomLayout(LoadCustomLayout),
@@ -1036,6 +1064,11 @@ enum SubCommand {
StaticConfigSchema,
/// Generates a static configuration JSON file based on the current window manager state
GenerateStaticConfig,
/// Generates the komorebi.lnk shortcut in shell:startup to autostart komorebi
#[clap(arg_required_else_help = true)]
EnableAutostart(EnableAutostart),
/// Deletes the komorebi.lnk shortcut in shell:startup to disable autostart
DisableAutostart,
}
pub fn send_message(bytes: &[u8]) -> Result<()> {
@@ -1044,11 +1077,61 @@ pub fn send_message(bytes: &[u8]) -> Result<()> {
Ok(stream.write_all(bytes)?)
}
fn startup_dir() -> Result<PathBuf> {
let home_dir = dirs::home_dir().expect("unable to obtain user's home folder");
let app_data = home_dir.join("AppData");
let roaming = app_data.join("Roaming");
let microsoft = roaming.join("Microsoft");
let windows = microsoft.join("Windows");
let start_menu = windows.join("Start Menu");
let programs = start_menu.join("Programs");
let startup = programs.join("Startup");
if !startup.is_dir() {
std::fs::create_dir_all(&startup)?;
}
Ok(startup)
}
#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
fn main() -> Result<()> {
let opts: Opts = Opts::parse();
match opts.subcmd {
SubCommand::EnableAutostart(args) => {
let mut current_exe = std::env::current_exe().expect("unable to get exec path");
current_exe.pop();
let komorebic_exe = current_exe.join("komorebic.exe");
let startup_dir = startup_dir()?;
let shortcut_file = startup_dir.join("komorebi.lnk");
let mut arguments = format!("start --config {}", args.config);
if args.whkd {
arguments.push_str(" --whkd");
} else if args.ahk {
arguments.push_str(" --ahk");
}
Command::new("powershell")
.arg("-c")
.arg("$WshShell = New-Object -comObject WScript.Shell; $Shortcut = $WshShell.CreateShortcut($env:SHORTCUT_PATH); $Shortcut.TargetPath = $env:TARGET_PATH; $Shortcut.Arguments = $env:TARGET_ARGS; $Shortcut.Save()")
.env("SHORTCUT_PATH", shortcut_file.to_string_lossy().to_string())
.env("TARGET_PATH", komorebic_exe.to_string_lossy().to_string())
.env("TARGET_ARGS", arguments)
.output()?;
}
SubCommand::DisableAutostart => {
let startup_dir = startup_dir()?;
let shortcut_file = startup_dir.join("komorebi.lnk");
if shortcut_file.is_file() {
std::fs::remove_file(shortcut_file)?;
}
}
SubCommand::Check => {
let home = HOME_DIR.clone();
let home_lossy_string = home.to_string_lossy();
@@ -1391,10 +1474,22 @@ fn main() -> Result<()> {
)?;
}
SubCommand::Start(arg) => {
let mut ahk: String = String::from("autohotkey.exe");
if let Ok(komorebi_ahk_exe) = std::env::var("KOMOREBI_AHK_EXE") {
if which(&komorebi_ahk_exe).is_ok() {
ahk = komorebi_ahk_exe;
}
}
if arg.whkd && which("whkd").is_err() {
return Err(anyhow!("could not find whkd, please make sure it is installed before using the --whkd flag"));
}
if arg.ahk && which(&ahk).is_err() {
return Err(anyhow!("could not find autohotkey, please make sure it is installed before using the --ahk flag"));
}
let mut buf: PathBuf;
// The komorebi.ps1 shim will only exist in the Path if installed by Scoop
@@ -1426,10 +1521,10 @@ fn main() -> Result<()> {
}
flags.push(format!(
"'--config={}'",
"'--config=\"{}\"'",
path.as_os_str()
.to_string_lossy()
.trim_start_matches(r#"\\?\"#),
.trim_start_matches(r"\\?\"),
));
}
@@ -1445,8 +1540,13 @@ fn main() -> Result<()> {
flags.push(format!("'--tcp-port={port}'"));
}
let argument_list = flags.join(",");
let script = {
let script = if flags.is_empty() {
format!(
"Start-Process '{}' -WindowStyle hidden",
exec.unwrap_or("komorebi.exe")
)
} else {
let argument_list = flags.join(",");
format!(
"Start-Process '{}' -ArgumentList {argument_list} -WindowStyle hidden",
exec.unwrap_or("komorebi.exe")
@@ -1495,8 +1595,44 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
}
}
}
if arg.ahk {
let home = HOME_DIR.clone();
let mut config_ahk = home;
config_ahk.push("komorebi.ahk");
let script = format!(
r#"
Start-Process {ahk} {config} -WindowStyle hidden
"#,
config = config_ahk.as_os_str().to_string_lossy()
);
match powershell_script::run(&script) {
Ok(_) => {
println!("{script}");
}
Err(error) => {
println!("Error: {error}");
}
}
}
}
SubCommand::Stop => {
SubCommand::Stop(arg) => {
if arg.whkd {
let script = r#"
Stop-Process -Name:whkd -ErrorAction SilentlyContinue
"#;
match powershell_script::run(script) {
Ok(_) => {
println!("{script}");
}
Err(error) => {
println!("Error: {error}");
}
}
}
send_message(&SocketMessage::Stop.as_bytes()?)?;
}
SubCommand::FloatRule(arg) => {
@@ -1546,6 +1682,9 @@ if (!(Get-Process whkd -ErrorAction SilentlyContinue))
SubCommand::ChangeLayout(arg) => {
send_message(&SocketMessage::ChangeLayout(arg.default_layout).as_bytes()?)?;
}
SubCommand::CycleLayout(arg) => {
send_message(&SocketMessage::CycleLayout(arg.cycle_direction).as_bytes()?)?;
}
SubCommand::LoadCustomLayout(arg) => {
send_message(
&SocketMessage::ChangeLayoutCustom(resolve_windows_path(&arg.path)?).as_bytes()?,

View File

@@ -21,6 +21,22 @@
}
]
},
"active_window_border_offset": {
"description": "Offset of the active window border (default: None)",
"type": [
"integer",
"null"
],
"format": "int32"
},
"active_window_border_width": {
"description": "Width of the active window border (default: 20)",
"type": [
"integer",
"null"
],
"format": "int32"
},
"alt_focus_hack": {
"description": "Always send the ALT key when using focus commands (default: false)",
"type": [
@@ -36,7 +52,7 @@
]
},
"border_offset": {
"description": "Offset of the active window border (default: None)",
"description": "DEPRECATED from v0.1.19: use active_window_border_offset instead",
"anyOf": [
{
"$ref": "#/definitions/Rect"
@@ -57,7 +73,7 @@
}
},
"border_width": {
"description": "Width of the active window border (default: 20)",
"description": "DEPRECATED from v0.1.19: use active_window_border_width instead",
"type": [
"integer",
"null"
@@ -154,6 +170,16 @@
"$ref": "#/definitions/IdWithIdentifier"
}
},
"monitor_index_preferences": {
"description": "Set monitor index preferences",
"type": [
"object",
"null"
],
"additionalProperties": {
"$ref": "#/definitions/Rect"
}
},
"monitors": {
"description": "Monitor and workspace configurations",
"type": [
@@ -342,9 +368,30 @@
},
"kind": {
"$ref": "#/definitions/ApplicationIdentifier"
},
"matching_strategy": {
"anyOf": [
{
"$ref": "#/definitions/MatchingStrategy"
},
{
"type": "null"
}
]
}
}
},
"MatchingStrategy": {
"type": "string",
"enum": [
"Legacy",
"Equals",
"StartsWith",
"EndsWith",
"Contains",
"Regex"
]
},
"MonitorConfig": {
"type": "object",
"required": [
@@ -571,4 +618,4 @@
}
}
}
}
}