Compare commits

..

46 Commits

Author SHA1 Message Date
LGUG2Z
c62ddb3c42 chore(release): v0.1.7 2022-01-17 08:19:09 -08:00
LGUG2Z
87e8eb48a6 fix(wm): update state for any new float rules
This update ensures that whenever a new float rule is added, the focused
workspaces on all monitors will be checked to see if there are any
currently managed windows which match that rule. If so, the matching
window(s) will be removed from the workspace and the workspace will be
updated.

Matching windows on non-focused workspaces will not be removed, as these
windows may be hidden, and removing them could result in these windows
being inaccessible, requiring them to be killed before they can be
relaunched

fix #93
2022-01-17 08:13:56 -08:00
LGUG2Z
5f1356b3e2 docs(readme): update link to aik2's post 2022-01-14 08:17:09 -08:00
LGUG2Z
00df672352 chore(deps): bump heck from 0.3.3 to 0.4.0 2022-01-13 14:13:12 -08:00
dependabot[bot]
2b83ff8148 chore(deps): bump sysinfo from 0.21.2 to 0.22.4
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.21.2 to 0.22.4.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-13 14:09:12 -08:00
LGUG2Z
749e247d85 chore(deps): bump windows-rs to 0.30 2022-01-13 14:08:43 -08:00
LGUG2Z
e70086b681 docs(readme): add links to articles, blogs etc 2022-01-11 07:37:39 -08:00
LGUG2Z
39685dd615 feat(wm): add cmd to move ws to other monitors
This commit adds a new komorebic command to move the entire focused
workspace and all managed windows and containers to a target monitor
index. Windows that have been excluded from management using various
rules will not be moved as they are not tracked in the window manager
state.

resolve #88
2022-01-07 08:52:18 -08:00
LGUG2Z
228ef78d7f chore(deps): bump clap to 3.0.0 2021-12-31 15:19:14 -08:00
LGUG2Z
e2ae9b1207 chore(deps): bump clap to latest rc 2021-12-31 08:33:29 -08:00
LGUG2Z
5e3f1cbb44 fix(wm): send state errors to komorebic
This commit ensures that errors are sent to komorebic in response to the
state command if they occur, so that komorebic is not left hanging
indefinitely waiting for a successful response that will never come.
2021-12-06 16:43:22 -08:00
LGUG2Z
9fd4dbf044 fix(wm): handle empty vd reg values gracefully
This commit fixes a regression introduced in
85fe20ebba, where running komorebi before
creating and interacting with virtual desktops via the task view on
Windows 10 would cause komorebi to panic when it could not find the
CurrentVirtualDesktop key in the registry, as it only gets populated
after interacting with virtual desktops via the task view in a new
session.
2021-12-03 22:24:50 -08:00
LGUG2Z
9be248bc03 feat(wm): add cmd to focus ws on target monitor
This commit adds a new command which allows the focusing of workspaces
on monitors other than the currently focused monitor by specifying a
monitor index.

Sending this command to komorebi will make the target monitor index the
currently focused monitor.

resolve #85
2021-12-03 14:52:14 -08:00
LGUG2Z
85fe20ebba refactor(wm): validate virtual desktops via reg
This commit refactors the validations that ensure that only commands and
events originating on the same virtual desktop that komorebi was started
on are managed.

This was previously handled by the winvd crate which relied on
undocumented APIs that broke as of Windows 11. This method, while not
very elegant, seems like the best solution for now.

In short, komorebi checks the registry (which has different paths on
Win10 and Win11...) to keep track of the current virtual desktop id.

This is problematic because we just end up comparing byte arrays, and
there is no meaningful representation of the ids that are being
compared, not even a GUID.  Nevertheless, it works and it ensures that
komorebi is limited to operating on a single virtual desktop.

resolve #77
2021-12-02 18:44:44 -08:00
LGUG2Z
409d374b72 fix(wm): enforce virtual desktop validation
This commit fixes a regression introduced in
2e86b607b2
which broke virtual desktop id validation on Windows 10.
2021-12-02 15:59:09 -08:00
dependabot[bot]
1fb0a7cd6e chore(deps): bump tracing-subscriber from 0.3.1 to 0.3.3
Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from 0.3.1 to 0.3.3.
- [Release notes](https://github.com/tokio-rs/tracing/releases)
- [Commits](https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.1...tracing-subscriber-0.3.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 11:51:46 -08:00
dependabot[bot]
d0e46515c5 chore(deps): bump strum from 0.21.0 to 0.23.0
Bumps [strum](https://github.com/Peternator7/strum) from 0.21.0 to 0.23.0.
- [Release notes](https://github.com/Peternator7/strum/releases)
- [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Peternator7/strum/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 11:51:31 -08:00
dependabot[bot]
e01bbd9f74 chore(deps): bump sysinfo from 0.20.5 to 0.21.1
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.20.5 to 0.21.1.
- [Release notes](https://github.com/GuillaumeGomez/sysinfo/releases)
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 09:00:33 -08:00
dependabot[bot]
be53ea2c24 chore(deps): bump syn from 1.0.81 to 1.0.82
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.81 to 1.0.82.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.81...1.0.82)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 09:00:16 -08:00
dependabot[bot]
d49279e888 chore(deps): bump getset from 0.1.1 to 0.1.2
Bumps [getset](https://github.com/Hoverbear/getset) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/Hoverbear/getset/releases)
- [Commits](https://github.com/Hoverbear/getset/compare/0.1.1...0.1.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 09:00:05 -08:00
dependabot[bot]
daa2912945 chore(deps): bump serde_json from 1.0.71 to 1.0.72
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.71 to 1.0.72.
- [Release notes](https://github.com/serde-rs/json/releases)
- [Commits](https://github.com/serde-rs/json/compare/v1.0.71...v1.0.72)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 08:59:36 -08:00
dependabot[bot]
2c515d54f7 chore(deps): bump miow from 0.3.7 to 0.4.0
Bumps [miow](https://github.com/yoshuawuyts/miow) from 0.3.7 to 0.4.0.
- [Release notes](https://github.com/yoshuawuyts/miow/releases)
- [Changelog](https://github.com/yoshuawuyts/miow/blob/master/CHANGELOG.md)
- [Commits](https://github.com/yoshuawuyts/miow/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-01 08:59:18 -08:00
LGUG2Z
f9785bef55 feat(wm): add option to either minimize or hide
This commit adds a command to let the user decide if they want windows
to be hidden with SW_HIDE or minimized with SW_MINIMIZE when workspaces
are changed or window container stacks are cycled.

After a modest amount of local testing, SW_MINIMIZE does not appear to
introduce any regressions, and given that alt-tabbing is a common
workflow on Windows, it makes sense to have minimizing be the default
setting to ease the onboarding experience for new users.

resolve #72
2021-11-20 15:18:45 -08:00
LGUG2Z
0519ebddbf fix(wm): ignore mstsc.exe helper classes on w11
This commit adds OPContainerClass and IHWindowClass to the
FLOAT_IDENTIFIERS global vec, to ignore by default the extra invisible
input and output handling windows created by mstsc.exe when WSL is
launched on Windows 11.

fix #74
2021-11-20 08:32:43 -08:00
LGUG2Z
b1ca0a3e3c feat(wm): enforce last known layout on unpause
This commit ensures that the last known layout is restored on a
workspace when a command to unpause has been received.
2021-11-19 16:34:38 -08:00
LGUG2Z
84ccfedad4 chore(deps): bump windows-rs from 0.26 to 0.28 2021-11-18 14:09:54 -08:00
LGUG2Z
adcb38fed9 chore(deps): bump windows-rs from 0.25 to 0.26 2021-11-15 12:49:51 -08:00
LGUG2Z
4a19edaab2 feat(wm): impl drag to move in append mode
This commit ensures that when a window is dragged over another window container while
WindowContainerBehaviour::Append is set, the window will be removed from its current
container and appended to the target container instead of swapping the positions of the two
containers, as would be the case for WindowContainerBehaviour::Create.

re #72
2021-11-15 12:42:49 -08:00
LGUG2Z
676b643faf feat(wm): add cmd to toggle new window behaviour
This commit introduces a new command, toggle-new-window-behaviour, which
can be used to toggle how new windows on the screen will be handled.

The default setting is to add a new window in a dedicated container, but
when toggled, new windows will be stacked on top of the currently
focused window container.

This can be useful if you only want to use a certain number of columns,
and when you have enough windows on the screen for them, you can toggle
the new window behaviour to start appending to the existing column
stacks.

This commit also fixes a bug where stacked windows being closed did
cause the next window underneath in the stack to be shown.

re #72
2021-11-13 14:31:02 -08:00
LGUG2Z
7f74640dbd chore(deps): bump windows-rs and serde-json 2021-11-05 15:22:45 -07:00
LGUG2Z
c247426b8e ci(artifacts): include .pdb files in archives
resolve #70
2021-11-03 11:10:32 -07:00
LGUG2Z
4d7ccc5519 feat(wm): allow resize-axis for custom primary col
This commit allows the resize-axis cmd on Axis::Horizontal to operate on
the Primary column of a CustomLayout.

Note that this will only operate on a CustomLayout that has met the
window count threshold to enable the tertiary column. If it has not, the
layout will render as DefaultLayout::Columns, which does not support the
resize-axis cmd.
2021-11-03 10:04:51 -07:00
LGUG2Z
71e28b33e3 feat(wm): add resize-delta cmd
This commit adds a command to set the resize delta used under the hood
by the resize-edge and resize-axis commands. The resize delta defaults
to 50 pixels as was hard-coded previously.
2021-11-02 14:16:29 -07:00
LGUG2Z
40226a2bbd refactor(wm): update cmds & keep compat w/ aliases
This commit updates a number of komorebic subcommand names while
maintaining their old names as aliases in order to preserve backwards
compatibility.

Resize becomes ResizeEdge, to complement ResizeAxis, and all of the
commands for saving and loading BSP resize adjustments (QuickSave,
QuickLoad, Save, Load) are now post-fixed with "Resize" in order to make
it clear that these commands are not related to custom layout saving and
loading.
2021-11-02 14:16:29 -07:00
LGUG2Z
2814349228 feat(wm): add cmd to resize by axis
This commit adds a new command to resize by axis. Resizing is still
limited to the BSP layout. This command is intended to be bound to mouse
wheel up and down events, with different modified keys determining the
axis to operate on.
2021-11-02 14:16:29 -07:00
LGUG2Z
d627a1a771 chore(deps): bump windows-rs from 0.23 to 0.24 2021-11-02 13:24:23 -07:00
LGUG2Z
78683ce7b3 chore(deps): bump clap from 3.0.0-beta.4 to beta.5 2021-11-01 14:56:42 -07:00
LGUG2Z
a1ca4f03c3 chore(deps): bump tracing-subscriber and tracing-appender 2021-11-01 14:45:36 -07:00
dependabot[bot]
147a56c274 chore(deps): bump syn from 1.0.80 to 1.0.81
Bumps [syn](https://github.com/dtolnay/syn) from 1.0.80 to 1.0.81.
- [Release notes](https://github.com/dtolnay/syn/releases)
- [Commits](https://github.com/dtolnay/syn/compare/1.0.80...1.0.81)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-01 14:39:13 -07:00
LGUG2Z
127254b7ac feat(wm): disable windows ffm on stop and ctrl-c
This commit disables the native Windows ffm implementation as part of
the stop command and the ctrl-c handler.

resolve #69
2021-11-01 13:59:40 -07:00
LGUG2Z
4e6e2b3aa8 feat(wm): mouse follows focus enable/disable cmd
This commit adds a command to explicitly specify the desired state of
mouse follows focus to complement the previously added toggle command.
2021-10-30 16:05:04 -07:00
LGUG2Z
a55069df48 feat(wm): add mouse follows focus toggle
This commit adds a toggle for the mouse follows focus behaviour that has
been the default for komorebi until now.

resolve #63
2021-10-29 13:41:55 -07:00
LGUG2Z
7fd545ca35 fix(wm): handle cross-monitor drag/move events
This commit ensures that when a window is dragged across a monitor
boundary, the ownership of the window container will be transferred to
the target monitor's currently focused workspace.

In order to achieve this, a new WindowManagerEvent variant has been
added, MoveResizeStart, which will store an optional pending_move_op on
the WindowManager struct. This must be consumed at the beginning of the
handler for MoveResizeEnd.

This is necessary because as soon as the window is dragged across a
monitor boundary, an event is sent (and handled) to update the currently
focused monitor and workspace as the target monitor and workspace, and
we still need to have the information about the original monitor,
workspace and container in order to make comparisons and ultimately
remove the origin container to be able to transfer it.

fix #58
2021-10-29 09:32:36 -07:00
LGUG2Z
14e63292e1 chore(deps): bump windows from 0.22 to 0.23 2021-10-28 14:15:21 -07:00
LGUG2Z
18f34babfa chore(deps): bump windows from 0.21 to 0.22
This commit bumps the version of the windows-rs and deprecates the
bindings crate in favour of using the pre-packaged APIs that are
available as of 0.22.
2021-10-28 09:01:10 -07:00
LGUG2Z
2f7ae6f15f build(just): add justfile for common tasks 2021-10-27 19:21:54 -07:00
35 changed files with 1728 additions and 1065 deletions

View File

@@ -83,6 +83,8 @@ jobs:
path: |
target/${{ matrix.target }}/release/komorebi.exe
target/${{ matrix.target }}/release/komorebic.exe
target/${{ matrix.target }}/release/komorebi.pdb
target/${{ matrix.target }}/release/komorebic.pdb
retention-days: 7
- name: Generate changelog
if: startsWith(github.ref, 'refs/tags/')

457
Cargo.lock generated
View File

@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "addr2line"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
@@ -45,9 +45,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
version = "0.3.61"
version = "0.3.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6"
dependencies = [
"addr2line",
"cc",
@@ -58,13 +58,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "bindings"
version = "0.1.0"
dependencies = [
"windows",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -73,9 +66,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.71"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
[[package]]
name = "cfg-if"
@@ -89,24 +82,11 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi 0.3.9",
]
[[package]]
name = "clap"
version = "3.0.0-beta.4"
version = "3.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406"
checksum = "31b34190c12bd1d613deba77e1cc13e68eaf4a0d51e389dbd485b7bfe15a47c0"
dependencies = [
"atty",
"bitflags",
@@ -116,17 +96,17 @@ dependencies = [
"os_str_bytes",
"strsim",
"termcolor",
"terminal_size",
"textwrap",
"vec_map",
]
[[package]]
name = "clap_derive"
version = "3.0.0-beta.4"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac"
checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153"
dependencies = [
"heck",
"heck 0.4.0",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -160,43 +140,6 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "com"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a30a2b2a013da986dc5cc3eda3d19c0d59d53f835be1b2356eb8d00f000c793"
dependencies = [
"com_macros",
]
[[package]]
name = "com_macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7606b05842fea68ddcc89e8053b8860ebcb2a0ba8d6abfe3a148e5d5a8d3f0c1"
dependencies = [
"com_macros_support",
"proc-macro2",
"syn",
]
[[package]]
name = "com_macros_support"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97e9a6d20f4ac8830e309a455d7e9416e65c6af5a97c88c55fbb4c2012e107da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "const-sha1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
@@ -205,9 +148,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -226,9 +169,9 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.5"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@@ -239,9 +182,9 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
@@ -286,12 +229,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "dtoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
[[package]]
name = "either"
version = "1.6.1"
@@ -378,9 +315,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "getrandom"
version = "0.2.3"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
dependencies = [
"cfg-if 1.0.0",
"libc",
@@ -389,9 +326,9 @@ dependencies = [
[[package]]
name = "getset"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9"
dependencies = [
"proc-macro-error",
"proc-macro2",
@@ -401,9 +338,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.25.0"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "hashbrown"
@@ -420,6 +357,12 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "heck"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -447,9 +390,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
dependencies = [
"autocfg",
"hashbrown",
@@ -477,9 +420,9 @@ dependencies = [
[[package]]
name = "instant"
version = "0.1.11"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
]
@@ -499,6 +442,12 @@ version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@@ -511,9 +460,8 @@ dependencies = [
[[package]]
name = "komorebi"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"bindings",
"bitflags",
"clap",
"color-eyre",
@@ -525,7 +473,7 @@ dependencies = [
"hotwatch",
"komorebi-core",
"lazy_static",
"miow 0.3.7",
"miow 0.4.0",
"nanoid",
"parking_lot",
"paste",
@@ -535,43 +483,44 @@ dependencies = [
"sysinfo",
"tracing",
"tracing-appender",
"tracing-subscriber",
"tracing-subscriber 0.3.6",
"uds_windows",
"which",
"windows",
"winput",
"winvd",
"winreg",
]
[[package]]
name = "komorebi-core"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"bindings",
"clap",
"color-eyre",
"serde",
"serde_json",
"serde_yaml",
"strum",
"windows",
]
[[package]]
name = "komorebic"
version = "0.1.6"
version = "0.1.7"
dependencies = [
"bindings",
"clap",
"color-eyre",
"derive-ahk",
"dirs",
"fs-tail",
"heck",
"heck 0.4.0",
"komorebi-core",
"paste",
"powershell_script",
"serde",
"serde_json",
"uds_windows",
"windows",
]
[[package]]
@@ -588,9 +537,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.103"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "linked-hash-map"
@@ -618,9 +567,9 @@ dependencies = [
[[package]]
name = "matchers"
version = "0.0.1"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
@@ -633,9 +582,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
@@ -695,11 +644,11 @@ dependencies = [
[[package]]
name = "miow"
version = "0.3.7"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e"
dependencies = [
"winapi 0.3.9",
"windows-sys",
]
[[package]]
@@ -724,9 +673,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.23.0"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
dependencies = [
"bitflags",
"cc",
@@ -762,30 +711,11 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
@@ -793,24 +723,27 @@ dependencies = [
[[package]]
name = "object"
version = "0.26.2"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "os_str_bytes"
version = "3.1.0"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
dependencies = [
"memchr",
]
[[package]]
name = "owo-colors"
@@ -848,9 +781,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "petgraph"
@@ -864,9 +797,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]]
name = "powershell_script"
@@ -876,9 +809,9 @@ checksum = "36d62894f5590e88d99d0d82918742ba8e5bff1985af15d4906b6a65f635adb2"
[[package]]
name = "ppv-lite86"
version = "0.2.14"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-error"
@@ -906,18 +839,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.30"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
"proc-macro2",
]
@@ -1083,10 +1016,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "ryu"
version = "1.0.5"
name = "rustversion"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "same-file"
@@ -1105,18 +1044,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
"proc-macro2",
"quote",
@@ -1125,23 +1064,23 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.68"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79"
dependencies = [
"itoa",
"itoa 1.0.1",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.21"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af"
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
dependencies = [
"dtoa",
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
@@ -1163,9 +1102,9 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
[[package]]
name = "smallvec"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "strsim"
@@ -1175,30 +1114,31 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum"
version = "0.21.0"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb"
dependencies = [
"strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.21.1"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
dependencies = [
"heck",
"heck 0.3.3",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "1.0.80"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
"proc-macro2",
"quote",
@@ -1207,9 +1147,9 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.20.5"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e223c65cd36b485a34c2ce6e38efa40777d31c4166d9076030c74cdcf971679f"
checksum = "7f1bfab07306a27332451a662ca9c8156e3a9986f82660ba9c8e744fe8455d43"
dependencies = [
"cfg-if 1.0.0",
"core-foundation-sys",
@@ -1239,13 +1179,23 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi 0.3.9",
]
[[package]]
name = "textwrap"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
dependencies = [
"unicode-width",
"terminal_size",
]
[[package]]
@@ -1270,12 +1220,12 @@ dependencies = [
[[package]]
name = "time"
version = "0.1.43"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
dependencies = [
"itoa 0.4.8",
"libc",
"winapi 0.3.9",
]
[[package]]
@@ -1292,13 +1242,13 @@ dependencies = [
[[package]]
name = "tracing-appender"
version = "0.1.2"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9965507e507f12c8901432a33e31131222abac31edd90cabbcf85cf544b7127a"
checksum = "94571df2eae3ed4353815ea5a90974a594a1792d8782ff2cbcc9392d1101f366"
dependencies = [
"chrono",
"crossbeam-channel",
"tracing-subscriber",
"time",
"tracing-subscriber 0.3.6",
]
[[package]]
@@ -1328,7 +1278,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24"
dependencies = [
"tracing",
"tracing-subscriber",
"tracing-subscriber 0.2.25",
]
[[package]]
@@ -1342,36 +1292,33 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77be66445c4eeebb934a7340f227bfe7b338173d3f8c00a60a5a58005c9faecf"
dependencies = [
"ansi_term",
"chrono",
"lazy_static",
"matchers",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]
@@ -1390,29 +1337,17 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "walkdir"
@@ -1487,49 +1422,89 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.21.1"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f5f8d2ea79bf690bbee453fd4a1516ae426e5d5c7215d96cc0c3dc134fc4a0"
checksum = "b749ebd2304aa012c5992d11a25d07b406bdbe5f79d371cb7a918ce501a19eb0"
dependencies = [
"const-sha1",
"windows_gen",
"windows_macros",
"windows_reader",
"windows_aarch64_msvc 0.30.0",
"windows_i686_gnu 0.30.0",
"windows_i686_msvc 0.30.0",
"windows_x86_64_gnu 0.30.0",
"windows_x86_64_msvc 0.30.0",
]
[[package]]
name = "windows_gen"
version = "0.21.1"
name = "windows-sys"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e6994f42f8481387778cc608407d6703410672d57f32a66009419d7a18aa912"
checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6"
dependencies = [
"windows_quote",
"windows_reader",
"windows_aarch64_msvc 0.28.0",
"windows_i686_gnu 0.28.0",
"windows_i686_msvc 0.28.0",
"windows_x86_64_gnu 0.28.0",
"windows_x86_64_msvc 0.28.0",
]
[[package]]
name = "windows_macros"
version = "0.21.1"
name = "windows_aarch64_msvc"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cc2357b1b03c19f056cb0e6d06011f80f54beadb4e36aee2ca98493c7cfc3c"
dependencies = [
"syn",
"windows_gen",
"windows_quote",
"windows_reader",
]
checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2"
[[package]]
name = "windows_quote"
version = "0.21.1"
name = "windows_aarch64_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cf987b5288c15e1997226848f78f3ed3ef8b78dcfd71a201c8c8684163a7e4d"
checksum = "29277a4435d642f775f63c7d1faeb927adba532886ce0287bd985bffb16b6bca"
[[package]]
name = "windows_reader"
version = "0.21.1"
name = "windows_i686_gnu"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "237b53e8b40766ea7db5da0d8c6c1442d21d0429f0ee7500d7b5688967bd9d7b"
checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a"
[[package]]
name = "windows_i686_gnu"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145e1989da93956c68d1864f32fb97c8f561a8f89a5125f6a2b7ea75524e4b8"
[[package]]
name = "windows_i686_msvc"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64"
[[package]]
name = "windows_i686_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a09e3a0d4753b73019db171c1339cd4362c8c44baf1bcea336235e955954a6"
[[package]]
name = "windows_x86_64_gnu"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954"
[[package]]
name = "windows_x86_64_gnu"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca64fcb0220d58db4c119e050e7af03c69e6f4f415ef69ec1773d9aab422d5a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f"
[[package]]
name = "windows_x86_64_msvc"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08cabc9f0066848fef4bc6a1c1668e6efce38b661d2aeec75d18d8617eebb5f1"
[[package]]
name = "winput"
@@ -1541,14 +1516,12 @@ dependencies = [
]
[[package]]
name = "winvd"
version = "0.0.20"
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bab2d5c745381b9c72797230150ec62244e693064fa0d654b5c4e6c75132a56"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"com",
"crossbeam-channel",
"once_cell",
"winapi 0.3.9",
]
[[package]]

View File

@@ -1,7 +1,6 @@
[workspace]
members = [
"bindings",
"derive-ahk",
"komorebi",
"komorebi-core",

173
README.md
View File

@@ -22,6 +22,11 @@ There is a [Discord server](https://discord.gg/vzBmPm6RkQ) available for _komore
troubleshooting etc. If you have any specific feature requests or bugs to report, please create an issue in this
repository.
Articles, blog posts, demos, and videos about _komorebi_ can be added to this list by PR:
- [Moving to Windows from Linux Pt 1](https://kvwu.io/posts/moving-to-windows/)
- [Windows 下的现代化平铺窗口管理器 komorebi](https://zhuanlan.zhihu.com/p/455064481)
## Description
_komorebi_ only responds to [WinEvents](https://docs.microsoft.com/en-us/windows/win32/winauto/event-constants) and the
@@ -188,6 +193,16 @@ passing it as an argument to the `--implementation` flag:
komorebic.exe toggle-focus-follows-mouse --implementation komorebi
```
#### Mouse Follows Focus
By default, the mouse will move to the center of the window when the focus is changed in a given direction. This
behaviour is know is 'mouse follows focus'. To disable this behaviour across all workspaces, add the following command
to your configuration file:
```ahk
Run, komorebic.exe toggle-mouse-follows-focus, , Hide
```
#### Saving and Loading Resized Layouts
If you create a BSP layout through various resize adjustments that you want to be able to restore easily in the future,
@@ -272,69 +287,77 @@ keybindings with. You can run `komorebic.exe <COMMAND> --help` to get a full exp
each command.
```
start Start komorebi.exe as a background process
stop Stop the komorebi.exe process and restore all hidden windows
state Show a JSON representation of the current window manager state
query Query the current window manager state
subscribe Subscribe to komorebi events
unsubscribe Unsubscribe from komorebi events
log Tail komorebi.exe's process logs (cancel with Ctrl-C)
quick-save Quicksave the current resize layout dimensions
quick-load Load the last quicksaved resize layout dimensions
save Save the current resize layout dimensions to a file
load Load the resize layout dimensions from a file
focus Change focus to the window in the specified direction
move Move the focused window in the specified direction
cycle-focus Change focus to the window in the specified cycle direction
cycle-move Move the focused window in the specified cycle direction
stack Stack the focused window in the specified direction
resize Resize the focused window in the specified direction
unstack Unstack the focused window
cycle-stack Cycle the focused stack in the specified cycle direction
move-to-monitor Move the focused window to the specified monitor
move-to-workspace Move the focused window to the specified workspace
send-to-monitor Send the focused window to the specified monitor
send-to-workspace Send the focused window to the specified workspace
focus-monitor Focus the specified monitor
focus-workspace Focus the specified workspace on the focused monitor
cycle-monitor Focus the monitor in the given cycle direction
cycle-workspace Focus the workspace in the given cycle direction
new-workspace Create and append a new workspace on the focused monitor
invisible-borders Set the invisible border dimensions around each window
work-area-offset Set offsets to exclude parts of the work area from tiling
adjust-container-padding Adjust container padding on the focused workspace
adjust-workspace-padding Adjust workspace padding on the focused workspace
change-layout Set the layout on the focused workspace
load-custom-layout Load a custom layout from file for the focused workspace
flip-layout Flip the layout on the focused workspace (BSP only)
promote Promote the focused window to the top of the tree
retile Force the retiling of all managed windows
ensure-workspaces Create at least this many workspaces for the specified monitor
container-padding Set the container padding for the specified workspace
workspace-padding Set the workspace padding for the specified workspace
workspace-layout Set the layout for the specified workspace
workspace-custom-layout Set a custom layout for the specified workspace
workspace-tiling Enable or disable window tiling for the specified workspace
workspace-name Set the workspace name for the specified workspace
toggle-pause Toggle the window manager on and off across all monitors
toggle-tiling Toggle window tiling on the focused workspace
toggle-float Toggle floating mode for the focused window
toggle-monocle Toggle monocle mode for the focused container
toggle-maximize Toggle native maximization for the focused window
restore-windows Restore all hidden windows (debugging command)
manage Force komorebi to manage the focused window
unmanage Unmanage a window that was forcibly managed
reload-configuration Reload ~/komorebi.ahk (if it exists)
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
float-rule Add a rule to always float the specified application
manage-rule Add a rule to always manage the specified application
workspace-rule Add a rule to associate an application with a workspace
identify-tray-application Identify an application that closes to the system tray
identify-border-overflow Identify an application that has overflowing borders
focus-follows-mouse Enable or disable focus follows mouse for the operating system
toggle-focus-follows-mouse Toggle focus follows mouse for the operating system
ahk-library Generate a library of AutoHotKey helper functions
help Print this message or the help of the given subcommand(s)
start Start komorebi.exe as a background process
stop Stop the komorebi.exe process and restore all hidden windows
state Show a JSON representation of the current window manager state
query Query the current window manager state
subscribe Subscribe to komorebi events
unsubscribe Unsubscribe from komorebi events
log Tail komorebi.exe's process logs (cancel with Ctrl-C)
quick-save-resize Quicksave the current resize layout dimensions
quick-load-resize Load the last quicksaved resize layout dimensions
save-resize Save the current resize layout dimensions to a file
load-resize Load the resize layout dimensions from a file
focus Change focus to the window in the specified direction
move Move the focused window in the specified direction
cycle-focus Change focus to the window in the specified cycle direction
cycle-move Move the focused window in the specified cycle direction
stack Stack the focused window in the specified direction
resize-edge Resize the focused window in the specified direction
resize-axis Resize the focused window or primary column along the specified axis
unstack Unstack the focused window
cycle-stack Cycle the focused stack in the specified cycle direction
move-to-monitor Move the focused window to the specified monitor
move-to-workspace Move the focused window to the specified workspace
send-to-monitor Send the focused window to the specified monitor
send-to-workspace Send the focused window to the specified workspace
focus-monitor Focus the specified monitor
focus-workspace Focus the specified workspace on the focused monitor
focus-monitor-workspace Focus the specified workspace on the target monitor
cycle-monitor Focus the monitor in the given cycle direction
cycle-workspace Focus the workspace in the given cycle direction
move-workspace-to-monitor Move the focused workspace to the specified monitor
new-workspace Create and append a new workspace on the focused monitor
resize-delta Set the resize delta (used by resize-edge and resize-axis)
invisible-borders Set the invisible border dimensions around each window
work-area-offset Set offsets to exclude parts of the work area from tiling
adjust-container-padding Adjust container padding on the focused workspace
adjust-workspace-padding Adjust workspace padding on the focused workspace
change-layout Set the layout on the focused workspace
load-custom-layout Load a custom layout from file for the focused workspace
flip-layout Flip the layout on the focused workspace (BSP only)
promote Promote the focused window to the top of the tree
retile Force the retiling of all managed windows
ensure-workspaces Create at least this many workspaces for the specified monitor
container-padding Set the container padding for the specified workspace
workspace-padding Set the workspace padding for the specified workspace
workspace-layout Set the layout for the specified workspace
workspace-custom-layout Set a custom layout for the specified workspace
workspace-tiling Enable or disable window tiling for the specified workspace
workspace-name Set the workspace name for the specified workspace
toggle-window-container-behaviour Toggle the behaviour for new windows (stacking or dynamic tiling)
toggle-pause Toggle window tiling on the focused workspace
toggle-tiling Toggle window tiling on the focused workspace
toggle-float Toggle floating mode for the focused window
toggle-monocle Toggle monocle mode for the focused container
toggle-maximize Toggle native maximization for the focused window
restore-windows Restore all hidden windows (debugging command)
manage Force komorebi to manage the focused window
unmanage Unmanage a window that was forcibly managed
reload-configuration Reload ~/komorebi.ahk (if it exists)
watch-configuration Enable or disable watching of ~/komorebi.ahk (if it exists)
window-hiding-behaviour Set the window behaviour when switching workspaces / cycling stacks
float-rule Add a rule to always float the specified application
manage-rule Add a rule to always manage the specified application
workspace-rule Add a rule to associate an application with a workspace
identify-tray-application Identify an application that closes to the system tray
identify-border-overflow Identify an application that has overflowing borders
focus-follows-mouse Enable or disable focus follows mouse for the operating system
toggle-focus-follows-mouse Toggle focus follows mouse for the operating system
mouse-follows-focus Enable or disable mouse follows focus on all workspaces
toggle-mouse-follows-focus Toggle mouse follows focus on all workspaces
ahk-library Generate a library of AutoHotKey helper functions
help Print this message or the help of the given subcommand(s)
```
### AutoHotKey Helper Library for `komorebic`
@@ -359,8 +382,11 @@ used [is available here](komorebi.sample.with.lib.ahk).
- [x] Move focused window container to workspace follow
- [x] Send focused window container to monitor
- [x] Send focused window container to workspace
- [x] Move focused workspace to monitor
- [x] Mouse follows focused container
- [x] Resize window container in direction
- [x] Resize window container on axis
- [x] Set custom resize delta
- [ ] Resize child window containers by split ratio
- [x] Quicksave and quickload layouts with resize dimensions
- [x] Save and load layouts with resize dimensions to/from specific files
@@ -385,6 +411,7 @@ used [is available here](komorebi.sample.with.lib.ahk).
- [x] Toggle floating windows
- [x] Toggle monocle window
- [x] Toggle native maximization
- [x] Toggle mouse follows focus
- [x] Toggle Xmouse/Windows focus follows mouse implementation
- [x] Toggle Komorebi focus follows mouse implementation (desktop and system tray-aware)
- [x] Toggle automatic tiling
@@ -470,16 +497,16 @@ Note that you do not have to include the full path of the named pipe, just the n
If the named pipe exists, `komorebi` will start pushing JSON data of successfully handled events and messages:
```json lines
{"event":{"type":"AddSubscriber","content":"yasb"},"state":{...}}
{"event":{"type":"FocusWindow","content":"Left"},"state":{...}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":131444,"title":"komorebi README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":13,"top":60,"right":1520,"bottom":1655}}]},"state":{...}}
{"event":{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":5572450,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]},"state":{...}}
{"event":{"type":"FocusWindow","content":"Right"},"state":{...}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}...}
{"event":{"type":"FocusWindow","content":"Down"},"state":{...}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":329264,"title":"den — Mozilla Firefox","exe":"firefox.exe","class":"MozillaWindowClass","rect":{"left":1539,"top":894,"right":1520,"bottom":821}}]},"state":{...}}
{"event":{"type":"FocusWindow","content":"Up"},"state":{...}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{...}}
{"event":{"type":"AddSubscriber","content":"yasb"},"state":{}}
{"event":{"type":"FocusWindow","content":"Left"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":131444,"title":"komorebi README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":13,"top":60,"right":1520,"bottom":1655}}]},"state":{}}
{"event":{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":5572450,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Right"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Down"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":329264,"title":"den — Mozilla Firefox","exe":"firefox.exe","class":"MozillaWindowClass","rect":{"left":1539,"top":894,"right":1520,"bottom":821}}]},"state":{}}
{"event":{"type":"FocusWindow","content":"Up"},"state":{}}
{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}}
```
You may then filter on the `type` key to listen to the events that you are interested in. For a full list of possible

View File

@@ -1,13 +0,0 @@
[package]
name = "bindings"
version = "0.1.0"
authors = ["Jade Iqbal"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
windows = "0.21"
[build-dependencies]
windows = "0.21"

View File

@@ -1,26 +0,0 @@
fn main() {
windows::build!(
Windows::Win32::Foundation::RECT,
Windows::Win32::Foundation::POINT,
Windows::Win32::Foundation::BOOL,
Windows::Win32::Foundation::PWSTR,
Windows::Win32::Foundation::HWND,
Windows::Win32::Foundation::LPARAM,
// error: `Windows.Win32.Graphics.Dwm.DWMWA_CLOAKED` not found in metadata
Windows::Win32::Graphics::Dwm::*,
// error: `Windows.Win32.Graphics.Gdi.MONITOR_DEFAULTTONEAREST` not found in metadata
Windows::Win32::Graphics::Gdi::*,
Windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS,
Windows::Win32::System::Threading::PROCESS_NAME_FORMAT,
Windows::Win32::System::Threading::OpenProcess,
Windows::Win32::System::Threading::QueryFullProcessImageNameW,
Windows::Win32::System::Threading::GetCurrentThreadId,
Windows::Win32::System::Threading::AttachThreadInput,
Windows::Win32::System::Threading::GetCurrentProcessId,
Windows::Win32::UI::KeyboardAndMouseInput::SetFocus,
Windows::Win32::UI::Accessibility::SetWinEventHook,
Windows::Win32::UI::Accessibility::HWINEVENTHOOK,
// error: `Windows.Win32.UI.WindowsAndMessaging.GWL_EXSTYLE` not found in metadata
Windows::Win32::UI::WindowsAndMessaging::*,
);
}

View File

@@ -1,4 +0,0 @@
pub use windows::Handle;
pub use windows::Result;
::windows::include_bindings!();

42
justfile Normal file
View File

@@ -0,0 +1,42 @@
set shell := ["cmd.exe", "/C"]
export RUST_BACKTRACE := "full"
clean:
cargo clean
fmt:
cargo +nightly fmt
cargo +nightly clippy
prettier --write README.md
install-komorebic:
cargo +stable install --path komorebic --locked
install-komorebi:
cargo +stable install --path komorebi --locked
install:
just install-komorebic
just install-komorebi
komorebic ahk-library
cat '%USERPROFILE%\komorebic.lib.ahk' > komorebic.lib.sample.ahk
run:
just install-komorebic
cargo +stable run --bin komorebi --locked
warn $RUST_LOG="warn":
just run
info $RUST_LOG="info":
just run
debug $RUST_LOG="debug":
just run
trace $RUST_LOG="trace":
just run
deadlock $RUST_LOG="trace":
just install-komorebic
cargo +stable run --bin komorebi --locked --features deadlock_detection

View File

@@ -1,16 +1,20 @@
[package]
name = "komorebi-core"
version = "0.1.6"
version = "0.1.7"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bindings = { package = "bindings", path = "../bindings" }
clap = "3.0.0-beta.4"
clap = { version = "3", features = ["derive"] }
color-eyre = "0.5"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.8"
strum = { version = "0.21", features = ["derive"] }
strum = { version = "0.23", features = ["derive"] }
[dependencies.windows]
version = "0.30"
features = [
"Win32_Foundation",
]

View File

@@ -19,7 +19,7 @@ pub trait Arrangement {
area: &Rect,
len: NonZeroUsize,
container_padding: Option<i32>,
layout_flip: Option<Flip>,
layout_flip: Option<Axis>,
resize_dimensions: &[Option<Rect>],
) -> Vec<Rect>;
}
@@ -31,7 +31,7 @@ impl Arrangement for DefaultLayout {
area: &Rect,
len: NonZeroUsize,
container_padding: Option<i32>,
layout_flip: Option<Flip>,
layout_flip: Option<Axis>,
resize_dimensions: &[Option<Rect>],
) -> Vec<Rect> {
let len = usize::from(len);
@@ -57,7 +57,7 @@ impl Arrangement for DefaultLayout {
let mut stack_left = area.left + primary_right;
match layout_flip {
Some(Flip::Horizontal | Flip::HorizontalAndVertical) if len > 1 => {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
main_left = main_left + area.right - primary_right;
stack_left = area.left;
}
@@ -99,7 +99,7 @@ impl Arrangement for DefaultLayout {
let mut stack_top = area.top + bottom;
match layout_flip {
Some(Flip::Vertical | Flip::HorizontalAndVertical) if len > 1 => {
Some(Axis::Vertical | Axis::HorizontalAndVertical) if len > 1 => {
main_top = main_top + area.bottom - bottom;
stack_top = area.top;
}
@@ -150,7 +150,7 @@ impl Arrangement for DefaultLayout {
let mut secondary = area.left;
match layout_flip {
Some(Flip::Horizontal | Flip::HorizontalAndVertical) if len > 1 => {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
primary = area.left;
secondary = area.left + primary_right;
}
@@ -165,7 +165,7 @@ impl Arrangement for DefaultLayout {
let mut stack = area.left + primary_right + secondary_right;
match layout_flip {
Some(Flip::Horizontal | Flip::HorizontalAndVertical) if len > 1 => {
Some(Axis::Horizontal | Axis::HorizontalAndVertical) if len > 1 => {
secondary = area.left + primary_right + secondary_right;
stack = area.left;
}
@@ -224,7 +224,7 @@ impl Arrangement for CustomLayout {
area: &Rect,
len: NonZeroUsize,
container_padding: Option<i32>,
_layout_flip: Option<Flip>,
_layout_flip: Option<Axis>,
_resize_dimensions: &[Option<Rect>],
) -> Vec<Rect> {
let mut dimensions = vec![];
@@ -343,7 +343,7 @@ impl Arrangement for CustomLayout {
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)]
#[strum(serialize_all = "snake_case")]
pub enum Flip {
pub enum Axis {
Horizontal,
Vertical,
HorizontalAndVertical,
@@ -478,7 +478,7 @@ fn recursive_fibonacci(
idx: usize,
count: usize,
area: &Rect,
layout_flip: Option<Flip>,
layout_flip: Option<Axis>,
resize_adjustments: Vec<Option<Rect>>,
) -> Vec<Rect> {
let mut a = *area;
@@ -502,21 +502,21 @@ fn recursive_fibonacci(
if let Some(flip) = layout_flip {
match flip {
Flip::Horizontal => {
Axis::Horizontal => {
main_x = resized.left + half_width + (half_width - half_resized_width);
alt_x = resized.left;
alt_y = resized.top + half_resized_height;
main_y = resized.top;
}
Flip::Vertical => {
Axis::Vertical => {
main_y = resized.top + half_height + (half_height - half_resized_height);
alt_y = resized.top;
main_x = resized.left;
alt_x = resized.left + half_resized_width;
}
Flip::HorizontalAndVertical => {
Axis::HorizontalAndVertical => {
main_x = resized.left + half_width + (half_width - half_resized_width);
alt_x = resized.left;
main_y = resized.top + half_height + (half_height - half_resized_height);

View File

@@ -2,6 +2,7 @@ use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::ops::Deref;
use std::ops::DerefMut;
use std::path::PathBuf;
use color_eyre::eyre::anyhow;
@@ -22,6 +23,12 @@ impl Deref for CustomLayout {
}
}
impl DerefMut for CustomLayout {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl CustomLayout {
pub fn from_path_buf(path: PathBuf) -> Result<Self> {
let invalid_filetype = anyhow!("custom layouts must be json or yaml files");
@@ -75,6 +82,14 @@ impl CustomLayout {
None
}
pub fn set_primary_width_percentage(&mut self, percentage: usize) {
for column in self.iter_mut() {
if let Column::Primary(Option::Some(ColumnWidth::WidthPercentage(current))) = column {
*current = percentage;
}
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
// A valid layout must have at least one column

View File

@@ -28,7 +28,7 @@ impl DefaultLayout {
resize: &Option<Rect>,
edge: OperationDirection,
sizing: Sizing,
step: Option<i32>,
delta: i32,
) -> Option<Rect> {
if !matches!(self, Self::BSP) {
return None;
@@ -37,7 +37,7 @@ impl DefaultLayout {
let max_divisor = 1.005;
let mut r = resize.unwrap_or_default();
let resize_step = step.unwrap_or(50);
let resize_delta = delta;
match edge {
OperationDirection::Left => match sizing {
@@ -52,65 +52,65 @@ impl DefaultLayout {
// with index 0. I don't think it's worth trying to defensively program
// against this; if people end up in this situation they are better off
// just hitting the retile command
let diff = ((r.left + -resize_step) as f32).abs();
let diff = ((r.left + -resize_delta) as f32).abs();
let max = unaltered.right as f32 / max_divisor;
if diff < max {
r.left += -resize_step;
r.left += -resize_delta;
}
}
Sizing::Decrease => {
let diff = ((r.left - -resize_step) as f32).abs();
let diff = ((r.left - -resize_delta) as f32).abs();
let max = unaltered.right as f32 / max_divisor;
if diff < max {
r.left -= -resize_step;
r.left -= -resize_delta;
}
}
},
OperationDirection::Up => match sizing {
Sizing::Increase => {
let diff = ((r.top + resize_step) as f32).abs();
let diff = ((r.top + resize_delta) as f32).abs();
let max = unaltered.bottom as f32 / max_divisor;
if diff < max {
r.top += -resize_step;
r.top += -resize_delta;
}
}
Sizing::Decrease => {
let diff = ((r.top - resize_step) as f32).abs();
let diff = ((r.top - resize_delta) as f32).abs();
let max = unaltered.bottom as f32 / max_divisor;
if diff < max {
r.top -= -resize_step;
r.top -= -resize_delta;
}
}
},
OperationDirection::Right => match sizing {
Sizing::Increase => {
let diff = ((r.right + resize_step) as f32).abs();
let diff = ((r.right + resize_delta) as f32).abs();
let max = unaltered.right as f32 / max_divisor;
if diff < max {
r.right += resize_step;
r.right += resize_delta;
}
}
Sizing::Decrease => {
let diff = ((r.right - resize_step) as f32).abs();
let diff = ((r.right - resize_delta) as f32).abs();
let max = unaltered.right as f32 / max_divisor;
if diff < max {
r.right -= resize_step;
r.right -= resize_delta;
}
}
},
OperationDirection::Down => match sizing {
Sizing::Increase => {
let diff = ((r.bottom + resize_step) as f32).abs();
let diff = ((r.bottom + resize_delta) as f32).abs();
let max = unaltered.bottom as f32 / max_divisor;
if diff < max {
r.bottom += resize_step;
r.bottom += resize_delta;
}
}
Sizing::Decrease => {
let diff = ((r.bottom - resize_step) as f32).abs();
let diff = ((r.bottom - resize_delta) as f32).abs();
let max = unaltered.bottom as f32 / max_divisor;
if diff < max {
r.bottom -= resize_step;
r.bottom -= resize_delta;
}
}
},

View File

@@ -12,7 +12,7 @@ use strum::Display;
use strum::EnumString;
pub use arrangement::Arrangement;
pub use arrangement::Flip;
pub use arrangement::Axis;
pub use custom_layout::CustomLayout;
pub use cycle_direction::CycleDirection;
pub use default_layout::DefaultLayout;
@@ -39,17 +39,21 @@ pub enum SocketMessage {
CycleFocusWindow(CycleDirection),
CycleMoveWindow(CycleDirection),
StackWindow(OperationDirection),
ResizeWindow(OperationDirection, Sizing),
ResizeWindowEdge(OperationDirection, Sizing),
ResizeWindowAxis(Axis, Sizing),
UnstackWindow,
CycleStack(CycleDirection),
MoveContainerToMonitorNumber(usize),
MoveContainerToWorkspaceNumber(usize),
SendContainerToMonitorNumber(usize),
SendContainerToWorkspaceNumber(usize),
MoveWorkspaceToMonitorNumber(usize),
Promote,
ToggleFloat,
ToggleMonocle,
ToggleMaximize,
ToggleWindowContainerBehaviour,
WindowHidingBehaviour(HidingBehaviour),
// Current Workspace Commands
ManageFocusedWindow,
UnmanageFocusedWindow,
@@ -57,7 +61,7 @@ pub enum SocketMessage {
AdjustWorkspacePadding(Sizing, i32),
ChangeLayout(DefaultLayout),
ChangeLayoutCustom(PathBuf),
FlipLayout(Flip),
FlipLayout(Axis),
// Monitor and Workspace Commands
EnsureWorkspaces(usize, usize),
NewWorkspace,
@@ -73,6 +77,7 @@ pub enum SocketMessage {
CycleFocusWorkspace(CycleDirection),
FocusMonitorNumber(usize),
FocusWorkspaceNumber(usize),
FocusMonitorWorkspaceNumber(usize, usize),
ContainerPadding(usize, usize, i32),
WorkspacePadding(usize, usize, i32),
WorkspaceTiling(usize, usize, bool),
@@ -84,17 +89,18 @@ pub enum SocketMessage {
WatchConfiguration(bool),
InvisibleBorders(Rect),
WorkAreaOffset(Rect),
ResizeDelta(i32),
WorkspaceRule(ApplicationIdentifier, String, usize, usize),
FloatRule(ApplicationIdentifier, String),
ManageRule(ApplicationIdentifier, String),
IdentifyTrayApplication(ApplicationIdentifier, String),
IdentifyBorderOverflow(ApplicationIdentifier, String),
RemoveTitleBar(ApplicationIdentifier, String),
ToggleTitleBars,
State,
Query(StateQuery),
FocusFollowsMouse(FocusFollowsMouseImplementation, bool),
ToggleFocusFollowsMouse(FocusFollowsMouseImplementation),
MouseFollowsFocus(bool),
ToggleMouseFollowsFocus,
AddSubscriber(String),
RemoveSubscriber(String),
}
@@ -137,6 +143,20 @@ pub enum FocusFollowsMouseImplementation {
Windows,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)]
#[strum(serialize_all = "snake_case")]
pub enum WindowContainerBehaviour {
Create,
Append,
}
#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)]
#[strum(serialize_all = "snake_case")]
pub enum HidingBehaviour {
Hide,
Minimize,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)]
#[strum(serialize_all = "snake_case")]
pub enum Sizing {

View File

@@ -7,7 +7,7 @@ use strum::Display;
use strum::EnumString;
use crate::direction::Direction;
use crate::Flip;
use crate::Axis;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)]
#[strum(serialize_all = "snake_case")]
@@ -29,23 +29,23 @@ impl OperationDirection {
}
}
fn flip(self, layout_flip: Option<Flip>) -> Self {
fn flip(self, layout_flip: Option<Axis>) -> Self {
layout_flip.map_or(self, |flip| match self {
Self::Left => match flip {
Flip::Horizontal | Flip::HorizontalAndVertical => Self::Right,
Flip::Vertical => self,
Axis::Horizontal | Axis::HorizontalAndVertical => Self::Right,
Axis::Vertical => self,
},
Self::Right => match flip {
Flip::Horizontal | Flip::HorizontalAndVertical => Self::Left,
Flip::Vertical => self,
Axis::Horizontal | Axis::HorizontalAndVertical => Self::Left,
Axis::Vertical => self,
},
Self::Up => match flip {
Flip::Vertical | Flip::HorizontalAndVertical => Self::Down,
Flip::Horizontal => self,
Axis::Vertical | Axis::HorizontalAndVertical => Self::Down,
Axis::Horizontal => self,
},
Self::Down => match flip {
Flip::Vertical | Flip::HorizontalAndVertical => Self::Up,
Flip::Horizontal => self,
Axis::Vertical | Axis::HorizontalAndVertical => Self::Up,
Axis::Horizontal => self,
},
})
}
@@ -54,7 +54,7 @@ impl OperationDirection {
pub fn destination(
self,
layout: &dyn Direction,
layout_flip: Option<Flip>,
layout_flip: Option<Axis>,
idx: usize,
len: NonZeroUsize,
) -> Option<usize> {

View File

@@ -1,7 +1,6 @@
use serde::Deserialize;
use serde::Serialize;
use bindings::Windows::Win32::Foundation::RECT;
use windows::Win32::Foundation::RECT;
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
pub struct Rect {

View File

@@ -1,6 +1,9 @@
#SingleInstance Force
#Include %A_ScriptDir%\komorebic.lib.ahk
; Default to minimizing windows when switching workspaces
WindowHidingBehaviour("minimize")
; Enable hot reloading of changes to this file
WatchConfiguration("enable")
@@ -34,7 +37,7 @@ WorkspaceTiling(0, 4, "disable") ; Everything floats here
; Configure floating rules
FloatRule("class", "SunAwtDialog") ; All the IntelliJ popups
FloatRule("title", "Control Panek")
FloatRule("title", "Control Panel")
FloatRule("class", "TaskManagerWindow")
FloatRule("exe", "Wally.exe")
FloatRule("exe", "wincompose.exe")

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebi"
version = "0.1.6"
version = "0.1.7"
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
description = "A tiling window manager for Windows"
categories = ["tiling-window-manager", "windows"]
@@ -11,11 +11,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bindings = { package = "bindings", path = "../bindings" }
komorebi-core = { path = "../komorebi-core" }
bitflags = "1"
clap = "3.0.0-beta.4"
clap = { version = "3", features = ["derive"] }
color-eyre = "0.5"
crossbeam-channel = "0.5"
crossbeam-utils = "0.8"
@@ -29,16 +28,29 @@ parking_lot = { version = "0.11", features = ["deadlock_detection"] }
paste = "1"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
strum = { version = "0.21", features = ["derive"] }
sysinfo = "0.20"
strum = { version = "0.23", features = ["derive"] }
sysinfo = "0.22"
tracing = "0.1"
tracing-appender = "0.1"
tracing-subscriber = "0.2"
tracing-appender = "0.2"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uds_windows = "1"
which = "4"
winput = "0.2"
winvd = "0.0.20"
miow = "0.3"
miow = "0.4"
winreg = "0.10"
[dependencies.windows]
version = "0.30"
features = [
"Win32_Foundation",
"Win32_Graphics_Dwm",
"Win32_Graphics_Gdi",
"Win32_System_Threading",
"Win32_System_RemoteDesktop",
"Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Accessibility",
"Win32_UI_WindowsAndMessaging"
]
[features]
deadlock_detection = []

View File

@@ -78,18 +78,18 @@ impl Container {
}
pub fn remove_window_by_idx(&mut self, idx: usize) -> Option<Window> {
self.windows_mut().remove(idx)
let window = self.windows_mut().remove(idx);
if idx != 0 {
self.focus_window(idx - 1);
};
window
}
pub fn remove_focused_window(&mut self) -> Option<Window> {
let focused_idx = self.focused_window_idx();
let window = self.remove_window_by_idx(focused_idx);
if focused_idx != 0 {
self.focus_window(focused_idx - 1);
}
window
self.remove_window_by_idx(focused_idx)
}
pub fn add_window(&mut self, window: Window) {

View File

@@ -6,6 +6,7 @@ use std::fs::File;
use std::io::Write;
use std::process::Command;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::AtomicU32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
#[cfg(feature = "deadlock_detection")]
@@ -13,7 +14,7 @@ use std::thread;
#[cfg(feature = "deadlock_detection")]
use std::time::Duration;
use clap::Clap;
use clap::Parser;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use crossbeam_channel::Receiver;
@@ -28,7 +29,10 @@ use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::EnvFilter;
use which::which;
use winreg::enums::HKEY_CURRENT_USER;
use winreg::RegKey;
use komorebi_core::HidingBehaviour;
use komorebi_core::SocketMessage;
use crate::process_command::listen_for_commands;
@@ -78,7 +82,12 @@ lazy_static! {
static ref WORKSPACE_RULES: Arc<Mutex<HashMap<String, (usize, usize)>>> =
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 FLOAT_IDENTIFIERS: Arc<Mutex<Vec<String>>> = 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()
]));
static ref BORDER_OVERFLOW_IDENTIFIERS: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref WSL2_UI_PROCESSES: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![
"X410.exe".to_string(),
@@ -87,13 +96,12 @@ lazy_static! {
]));
static ref SUBSCRIPTION_PIPES: Arc<Mutex<HashMap<String, File>>> =
Arc::new(Mutex::new(HashMap::new()));
// Use app-specific titlebar removal options where possible
// eg. Windows Terminal, IntelliJ IDEA, Firefox
static ref NO_TITLEBAR: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(vec![]));
static ref HIDING_BEHAVIOUR: Arc<Mutex<HidingBehaviour>> =
Arc::new(Mutex::new(HidingBehaviour::Minimize));
}
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
@@ -137,20 +145,23 @@ fn setup() -> Result<(WorkerGuard, WorkerGuard)> {
// occurred to be recorded.
std::panic::set_hook(Box::new(|panic| {
// If the panic has a source location, record it as structured fields.
if let Some(location) = panic.location() {
// On nightly Rust, where the `PanicInfo` type also exposes a
// `message()` method returning just the message, we could record
// just the message instead of the entire `fmt::Display`
// implementation, avoiding the duplciated location
tracing::error!(
message = %panic,
panic.file = location.file(),
panic.line = location.line(),
panic.column = location.column(),
);
} else {
tracing::error!(message = %panic);
}
panic.location().map_or_else(
|| {
tracing::error!(message = %panic);
},
|location| {
// On nightly Rust, where the `PanicInfo` type also exposes a
// `message()` method returning just the message, we could record
// just the message instead of the entire `fmt::Display`
// implementation, avoiding the duplciated location
tracing::error!(
message = %panic,
panic.file = location.file(),
panic.line = location.line(),
panic.column = location.column(),
);
},
);
}));
Ok((guard, color_guard))
@@ -194,6 +205,49 @@ pub fn load_configuration() -> Result<()> {
Ok(())
}
pub fn current_virtual_desktop() -> Option<Vec<u8>> {
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
// This is the path on Windows 10
let mut current = hkcu
.open_subkey(format!(
r#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\SessionInfo\{}\VirtualDesktops"#,
SESSION_ID.load(Ordering::SeqCst)
))
.ok()
.and_then(
|desktops| match desktops.get_raw_value("CurrentVirtualDesktop") {
Ok(current) => Option::from(current.bytes),
Err(_) => None,
},
);
// This is the path on Windows 11
if current.is_none() {
current = hkcu
.open_subkey(r#"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VirtualDesktops"#)
.ok()
.and_then(
|desktops| match desktops.get_raw_value("CurrentVirtualDesktop") {
Ok(current) => Option::from(current.bytes),
Err(_) => None,
},
);
}
// For Win10 users that do not use virtual desktops, the CurrentVirtualDesktop value will not
// exist until one has been created in the task view
// The registry value will also not exist on user login if virtual desktops have been created
// but the task view has not been initiated
// In both of these cases, we return None, and the virtual desktop validation will never run. In
// the latter case, if the user desires this validation after initiating the task view, komorebi
// should be restarted, and then when this // fn runs again for the first time, it will pick up
// the value of CurrentVirtualDesktop and validate against it accordingly
current
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum NotificationEvent {
@@ -264,7 +318,7 @@ fn detect_deadlocks() {
});
}
#[derive(Clap)]
#[derive(Parser)]
#[clap(author, about, version)]
struct Opts {
/// Allow the use of komorebi's custom focus-follows-mouse implementation
@@ -281,6 +335,9 @@ fn main() -> Result<()> {
let has_valid_args = arg_count == 1 || (arg_count == 2 && CUSTOM_FFM.load(Ordering::SeqCst));
if has_valid_args {
let session_id = WindowsApi::process_id_to_session_id()?;
SESSION_ID.store(session_id, Ordering::SeqCst);
let mut system = sysinfo::System::new_all();
system.refresh_processes();
@@ -331,7 +388,12 @@ fn main() -> Result<()> {
tracing::error!("received ctrl-c, restoring all hidden windows and terminating process");
wm.lock().restore_all_windows()?;
wm.lock().restore_all_windows();
if WindowsApi::focus_follows_mouse()? {
WindowsApi::disable_focus_follows_mouse()?;
}
std::process::exit(130);
}

View File

@@ -45,11 +45,11 @@ pub fn new(id: isize, size: Rect, work_area_size: Rect) -> Monitor {
}
impl Monitor {
pub fn load_focused_workspace(&mut self) -> Result<()> {
pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> {
let focused_idx = self.focused_workspace_idx();
for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() {
if i == focused_idx {
workspace.restore()?;
workspace.restore(mouse_follows_focus)?;
} else {
workspace.hide();
}
@@ -68,6 +68,20 @@ impl Monitor {
Ok(())
}
pub fn remove_workspace_by_idx(&mut self, idx: usize) -> Option<Workspace> {
if idx < self.workspaces().len() {
return self.workspaces_mut().remove(idx);
}
if idx == 0 {
self.workspaces_mut().push_back(Workspace::default());
} else {
self.focus_workspace(idx - 1).ok()?;
};
None
}
pub fn ensure_workspace_count(&mut self, ensure_count: usize) {
if self.workspaces().len() < ensure_count {
self.workspaces_mut()

View File

@@ -15,11 +15,18 @@ use miow::pipe::connect;
use parking_lot::Mutex;
use uds_windows::UnixStream;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis;
use komorebi_core::FocusFollowsMouseImplementation;
use komorebi_core::Layout;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::SocketMessage;
use komorebi_core::StateQuery;
use komorebi_core::WindowContainerBehaviour;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::window_manager;
use crate::window_manager::WindowManager;
@@ -29,9 +36,8 @@ use crate::NotificationEvent;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::CUSTOM_FFM;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDING_BEHAVIOUR;
use crate::MANAGE_IDENTIFIERS;
use crate::NO_TITLEBAR;
use crate::REMOVE_TITLEBARS;
use crate::SUBSCRIPTION_PIPES;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
@@ -64,7 +70,17 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
impl WindowManager {
#[tracing::instrument(skip(self))]
pub fn process_command(&mut self, message: SocketMessage) -> Result<()> {
self.validate_virtual_desktop_id();
if let Some(virtual_desktop_id) = &self.virtual_desktop_id {
if let Some(id) = current_virtual_desktop() {
if id != *virtual_desktop_id {
tracing::info!(
"ignoring events and commands while not on virtual desktop {:?}",
virtual_desktop_id
);
return Ok(());
}
}
}
match message {
SocketMessage::Promote => self.promote_container_to_front()?,
@@ -108,10 +124,57 @@ impl WindowManager {
manage_identifiers.push(id);
}
}
SocketMessage::FloatRule(_, id) => {
SocketMessage::FloatRule(identifier, id) => {
let mut float_identifiers = FLOAT_IDENTIFIERS.lock();
if !float_identifiers.contains(&id) {
float_identifiers.push(id);
float_identifiers.push(id.clone());
}
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let mut hwnds_to_purge = vec![];
for (i, monitor) in self.monitors().iter().enumerate() {
for container in monitor
.focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace"))?
.containers()
.iter()
{
for window in container.windows().iter() {
match identifier {
ApplicationIdentifier::Exe => {
if window.exe()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
ApplicationIdentifier::Class => {
if window.class()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
ApplicationIdentifier::Title => {
if window.title()? == id {
hwnds_to_purge.push((i, window.hwnd));
}
}
}
}
}
}
for (monitor_idx, hwnd) in hwnds_to_purge {
let monitor = self
.monitors_mut()
.get_mut(monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor"))?;
monitor
.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no focused workspace"))?
.remove_window(hwnd)?;
monitor.update_focused_workspace(offset, &invisible_borders)?;
}
}
SocketMessage::AdjustContainerPadding(sizing, adjustment) => {
@@ -132,6 +195,9 @@ impl WindowManager {
SocketMessage::SendContainerToMonitorNumber(monitor_idx) => {
self.move_container_to_monitor(monitor_idx, false)?;
}
SocketMessage::MoveWorkspaceToMonitorNumber(monitor_idx) => {
self.move_workspace_to_monitor(monitor_idx)?;
}
SocketMessage::TogglePause => {
if self.is_paused {
tracing::info!("resuming");
@@ -140,6 +206,7 @@ impl WindowManager {
}
self.is_paused = !self.is_paused;
self.retile_all(true)?;
}
SocketMessage::ToggleTiling => {
self.toggle_tiling()?;
@@ -152,13 +219,13 @@ impl WindowManager {
);
self.focus_monitor(monitor_idx)?;
self.update_focused_workspace(true)?;
self.update_focused_workspace(self.mouse_follows_focus)?;
}
SocketMessage::FocusMonitorNumber(monitor_idx) => {
self.focus_monitor(monitor_idx)?;
self.update_focused_workspace(true)?;
self.update_focused_workspace(self.mouse_follows_focus)?;
}
SocketMessage::Retile => self.retile_all()?,
SocketMessage::Retile => self.retile_all(false)?,
SocketMessage::FlipLayout(layout_flip) => self.flip_layout(layout_flip)?,
SocketMessage::ChangeLayout(layout) => self.change_workspace_layout_default(layout)?,
SocketMessage::ChangeLayoutCustom(path) => self.change_workspace_custom_layout(path)?,
@@ -205,14 +272,22 @@ impl WindowManager {
})?;
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
}
SocketMessage::FocusMonitorWorkspaceNumber(monitor_idx, workspace_idx) => {
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
}
SocketMessage::Stop => {
tracing::info!(
"received stop command, restoring all hidden windows and terminating process"
);
self.restore_all_windows()?;
self.restore_all_windows();
if WindowsApi::focus_follows_mouse()? {
WindowsApi::disable_focus_follows_mouse()?;
}
std::process::exit(0)
}
SocketMessage::EnsureWorkspaces(monitor_idx, workspace_count) => {
@@ -225,7 +300,12 @@ impl WindowManager {
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
}
SocketMessage::State => {
let state = serde_json::to_string_pretty(&window_manager::State::from(&*self))?;
let state = match serde_json::to_string_pretty(&window_manager::State::from(&*self))
{
Ok(state) => state,
Err(error) => error.to_string(),
};
let mut socket =
dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?;
socket.push("komorebic.sock");
@@ -258,8 +338,85 @@ impl WindowManager {
let mut stream = UnixStream::connect(&socket)?;
stream.write_all(response.as_bytes())?;
}
SocketMessage::ResizeWindow(direction, sizing) => {
self.resize_window(direction, sizing, Option::from(50))?;
SocketMessage::ResizeWindowEdge(direction, sizing) => {
self.resize_window(direction, sizing, self.resize_delta, true)?;
}
SocketMessage::ResizeWindowAxis(axis, sizing) => {
// If the user has a custom layout, allow for the resizing of the primary column
// with this signal
if let Layout::Custom(ref mut custom) = self.focused_workspace_mut()?.layout_mut() {
if matches!(axis, Axis::Horizontal) {
let percentage = custom
.primary_width_percentage()
.unwrap_or(100 / custom.len());
match sizing {
Sizing::Increase => custom.set_primary_width_percentage(percentage + 5),
Sizing::Decrease => custom.set_primary_width_percentage(percentage - 5),
}
}
// Otherwise proceed with the resizing logic for individual window containers in the
// assumed BSP layout
} else {
match axis {
Axis::Horizontal => {
self.resize_window(
OperationDirection::Left,
sizing,
self.resize_delta,
false,
)?;
self.resize_window(
OperationDirection::Right,
sizing,
self.resize_delta,
false,
)?;
}
Axis::Vertical => {
self.resize_window(
OperationDirection::Up,
sizing,
self.resize_delta,
false,
)?;
self.resize_window(
OperationDirection::Down,
sizing,
self.resize_delta,
false,
)?;
}
Axis::HorizontalAndVertical => {
self.resize_window(
OperationDirection::Left,
sizing,
self.resize_delta,
false,
)?;
self.resize_window(
OperationDirection::Right,
sizing,
self.resize_delta,
false,
)?;
self.resize_window(
OperationDirection::Up,
sizing,
self.resize_delta,
false,
)?;
self.resize_window(
OperationDirection::Down,
sizing,
self.resize_delta,
false,
)?;
}
}
}
self.update_focused_workspace(false)?;
}
SocketMessage::FocusFollowsMouse(mut implementation, enable) => {
if !CUSTOM_FFM.load(Ordering::SeqCst) {
@@ -380,11 +537,11 @@ impl WindowManager {
}
SocketMessage::InvisibleBorders(rect) => {
self.invisible_borders = rect;
self.retile_all()?;
self.retile_all(false)?;
}
SocketMessage::WorkAreaOffset(rect) => {
self.work_area_offset = Option::from(rect);
self.retile_all()?;
self.retile_all(false)?;
}
SocketMessage::QuickSave => {
let workspace = self.focused_workspace()?;
@@ -455,16 +612,28 @@ impl WindowManager {
let mut pipes = SUBSCRIPTION_PIPES.lock();
pipes.remove(&subscriber);
}
SocketMessage::RemoveTitleBar(_, id) => {
let mut identifiers = NO_TITLEBAR.lock();
if !identifiers.contains(&id) {
identifiers.push(id);
SocketMessage::MouseFollowsFocus(enable) => {
self.mouse_follows_focus = enable;
}
SocketMessage::ToggleMouseFollowsFocus => {
self.mouse_follows_focus = !self.mouse_follows_focus;
}
SocketMessage::ResizeDelta(delta) => {
self.resize_delta = delta;
}
SocketMessage::ToggleWindowContainerBehaviour => {
match self.window_container_behaviour {
WindowContainerBehaviour::Create => {
self.window_container_behaviour = WindowContainerBehaviour::Append;
}
WindowContainerBehaviour::Append => {
self.window_container_behaviour = WindowContainerBehaviour::Create;
}
}
}
SocketMessage::ToggleTitleBars => {
let current = REMOVE_TITLEBARS.load(Ordering::SeqCst);
REMOVE_TITLEBARS.store(!current, Ordering::SeqCst);
self.update_focused_workspace(false)?;
SocketMessage::WindowHidingBehaviour(behaviour) => {
let mut hiding_behaviour = HIDING_BEHAVIOUR.lock();
*hiding_behaviour = behaviour;
}
};

View File

@@ -10,7 +10,9 @@ use parking_lot::Mutex;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::WindowContainerBehaviour;
use crate::current_virtual_desktop;
use crate::notify_subscribers;
use crate::window_manager::WindowManager;
use crate::window_manager_event::WindowManagerEvent;
@@ -50,7 +52,17 @@ impl WindowManager {
return Ok(());
}
self.validate_virtual_desktop_id();
if let Some(virtual_desktop_id) = &self.virtual_desktop_id {
if let Some(id) = current_virtual_desktop() {
if id != *virtual_desktop_id {
tracing::info!(
"ignoring events and commands while not on virtual desktop {:?}",
virtual_desktop_id
);
return Ok(());
}
}
}
// Make sure we have the most recently focused monitor from any event
match event {
@@ -102,13 +114,25 @@ impl WindowManager {
window.raise()?;
self.has_pending_raise_op = false;
}
WindowManagerEvent::Minimize(_, window)
| WindowManagerEvent::Destroy(_, window)
| WindowManagerEvent::Unmanage(window) => {
WindowManagerEvent::Destroy(_, window) | WindowManagerEvent::Unmanage(window) => {
self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?;
}
WindowManagerEvent::Minimize(_, window) => {
let mut hide = false;
{
let programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
if !programmatically_hidden_hwnds.contains(&window.hwnd) {
hide = true;
}
}
if hide {
self.focused_workspace_mut()?.remove_window(window.hwnd)?;
self.update_focused_workspace(false)?;
}
}
WindowManagerEvent::Hide(_, window) => {
let mut hide = false;
// Some major applications unfortunately send the HIDE signal when they are being
@@ -201,14 +225,53 @@ impl WindowManager {
}
}
let behaviour = self.window_container_behaviour;
let workspace = self.focused_workspace_mut()?;
if !workspace.contains_window(window.hwnd) {
workspace.new_container_for_window(*window);
self.update_focused_workspace(false)?;
match behaviour {
WindowContainerBehaviour::Create => {
workspace.new_container_for_window(*window);
self.update_focused_workspace(false)?;
}
WindowContainerBehaviour::Append => {
workspace
.focused_container_mut()
.ok_or_else(|| anyhow!("there is no focused container"))?
.add_window(*window);
self.update_focused_workspace(true)?;
}
}
}
}
WindowManagerEvent::MoveResizeStart(_, _) => {
let monitor_idx = self.focused_monitor_idx();
let workspace_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace_idx();
let container_idx = self
.focused_monitor()
.ok_or_else(|| anyhow!("there is no monitor with this idx"))?
.focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace with this idx"))?
.focused_container_idx();
self.pending_move_op = Option::from((monitor_idx, workspace_idx, container_idx));
}
WindowManagerEvent::MoveResizeEnd(_, window) => {
// We need this because if the event ends on a different monitor,
// that monitor will already have been focused and updated in the state
let pending = self.pending_move_op;
// Always consume the pending move op whenever this event is handled
self.pending_move_op = None;
let target_monitor_idx = self
.monitor_idx_from_current_pos()
.ok_or_else(|| anyhow!("cannot get monitor idx from current position"))?;
let new_window_behaviour = self.window_container_behaviour;
let workspace = self.focused_workspace_mut()?;
if workspace
.floating_windows()
@@ -218,12 +281,32 @@ impl WindowManager {
return Ok(());
}
let focused_idx = workspace.focused_container_idx();
let focused_container_idx = workspace.focused_container_idx();
let mut new_position = WindowsApi::window_rect(window.hwnd())?;
let old_position = *workspace
.latest_layout()
.get(focused_idx)
.ok_or_else(|| anyhow!("there is no latest layout"))?;
let mut new_position = WindowsApi::window_rect(window.hwnd())?;
.get(focused_container_idx)
// If the move was to another monitor with an empty workspace, the
// workspace here will refer to that empty workspace, which won't
// have any latest layout set. We fall back to a Default for Rect
// which allows us to make a reasonable guess that the drag has taken
// place across a monitor boundary to an empty workspace
.unwrap_or(&Rect::default());
// This will be true if we have moved to an empty workspace on another monitor
let mut moved_across_monitors = old_position == Rect::default();
if let Some((origin_monitor_idx, _, _)) = pending {
// If we didn't move to another monitor with an empty workspace, it is
// still possible that we moved to another monitor with a populated workspace
if !moved_across_monitors {
// So we'll check if the origin monitor index and the target monitor index
// are different, if they are, we can set the override
moved_across_monitors = origin_monitor_idx != target_monitor_idx;
}
}
// Adjust for the invisible borders
new_position.left += invisible_borders.left;
@@ -238,16 +321,90 @@ impl WindowManager {
bottom: new_position.bottom - old_position.bottom,
};
let is_move = resize.right == 0 && resize.bottom == 0;
// If we have moved across the monitors, use that override, otherwise determine
// if a move has taken place by ruling out a resize
let is_move = moved_across_monitors || resize.right == 0 && resize.bottom == 0;
if is_move {
tracing::info!("moving with mouse");
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace.swap_containers(focused_idx, target_idx);
if moved_across_monitors {
if let Some((
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
)) = pending
{
let target_workspace_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace_idx();
let target_container_idx = self
.monitors()
.get(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace()
.ok_or_else(|| {
anyhow!("there is no focused workspace for this monitor")
})?
.container_idx_from_current_point()
// Default to 0 in the case of an empty workspace
.unwrap_or(0);
self.transfer_container(
(
origin_monitor_idx,
origin_workspace_idx,
origin_container_idx,
),
(
target_monitor_idx,
target_workspace_idx,
target_container_idx,
),
)?;
// We want to make sure both the origin and target monitors are updated,
// so that we don't have ghost tiles until we force an interaction on
// the origin monitor's focused workspace
self.focus_monitor(origin_monitor_idx)?;
self.focus_workspace(origin_workspace_idx)?;
self.update_focused_workspace(false)?;
self.focus_monitor(target_monitor_idx)?;
self.focus_workspace(target_workspace_idx)?;
self.update_focused_workspace(false)?;
}
None => self.update_focused_workspace(true)?,
// Here we handle a simple move on the same monitor which is treated as
// a container swap
} else {
match new_window_behaviour {
WindowContainerBehaviour::Create => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace
.swap_containers(focused_container_idx, target_idx);
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(self.mouse_follows_focus)?;
}
}
}
WindowContainerBehaviour::Append => {
match workspace.container_idx_from_current_point() {
Some(target_idx) => {
workspace.move_window_to_container(target_idx)?;
self.update_focused_workspace(false)?;
}
None => {
self.update_focused_workspace(self.mouse_follows_focus)?;
}
}
}
}
}
} else {
tracing::info!("resizing with mouse");
@@ -282,8 +439,8 @@ impl WindowManager {
ops.push(resize_op!(resize.bottom, <, OperationDirection::Down));
}
for (edge, sizing, step) in ops {
self.resize_window(edge, sizing, Option::from(step))?;
for (edge, sizing, delta) in ops {
self.resize_window(edge, sizing, delta, true)?;
}
self.update_focused_workspace(false)?;

View File

@@ -1,38 +1,37 @@
use bitflags::bitflags;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_DEFERERASE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_DRAWFRAME;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_FRAMECHANGED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_HIDEWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOACTIVATE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOCOPYBITS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOOWNERZORDER;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOREDRAW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOREPOSITION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOSENDCHANGING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_NOZORDER;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
use windows::Win32::UI::WindowsAndMessaging::SWP_ASYNCWINDOWPOS;
use windows::Win32::UI::WindowsAndMessaging::SWP_DEFERERASE;
use windows::Win32::UI::WindowsAndMessaging::SWP_DRAWFRAME;
use windows::Win32::UI::WindowsAndMessaging::SWP_FRAMECHANGED;
use windows::Win32::UI::WindowsAndMessaging::SWP_HIDEWINDOW;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOCOPYBITS;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOMOVE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOOWNERZORDER;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOREDRAW;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOREPOSITION;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSENDCHANGING;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOSIZE;
use windows::Win32::UI::WindowsAndMessaging::SWP_NOZORDER;
use windows::Win32::UI::WindowsAndMessaging::SWP_SHOWWINDOW;
bitflags! {
#[derive(Default)]
pub struct SetWindowPosition: u32 {
const ASYNC_WINDOW_POS = SWP_ASYNCWINDOWPOS.0;
const DEFER_ERASE = SWP_DEFERERASE.0;
const DRAW_FRAME = SWP_DRAWFRAME.0;
const FRAME_CHANGED = SWP_FRAMECHANGED.0;
const HIDE_WINDOW = SWP_HIDEWINDOW.0;
const NO_ACTIVATE = SWP_NOACTIVATE.0;
const NO_COPY_BITS = SWP_NOCOPYBITS.0;
const NO_MOVE = SWP_NOMOVE.0;
const NO_OWNER_Z_ORDER = SWP_NOOWNERZORDER.0;
const NO_REDRAW = SWP_NOREDRAW.0;
const NO_REPOSITION = SWP_NOREPOSITION.0;
const NO_SEND_CHANGING = SWP_NOSENDCHANGING.0;
const NO_SIZE = SWP_NOSIZE.0;
const NO_Z_ORDER = SWP_NOZORDER.0;
const SHOW_WINDOW = SWP_SHOWWINDOW.0;
const ASYNC_WINDOW_POS = SWP_ASYNCWINDOWPOS;
const DEFER_ERASE = SWP_DEFERERASE;
const DRAW_FRAME = SWP_DRAWFRAME;
const FRAME_CHANGED = SWP_FRAMECHANGED;
const HIDE_WINDOW = SWP_HIDEWINDOW;
const NO_ACTIVATE = SWP_NOACTIVATE;
const NO_COPY_BITS = SWP_NOCOPYBITS;
const NO_MOVE = SWP_NOMOVE;
const NO_OWNER_Z_ORDER = SWP_NOOWNERZORDER;
const NO_REDRAW = SWP_NOREDRAW;
const NO_REPOSITION = SWP_NOREPOSITION;
const NO_SEND_CHANGING = SWP_NOSENDCHANGING;
const NO_SIZE = SWP_NOSIZE;
const NO_Z_ORDER = SWP_NOZORDER;
const SHOW_WINDOW = SWP_SHOWWINDOW;
}
}

View File

@@ -1,122 +1,123 @@
use bitflags::bitflags;
use windows::Win32::UI::WindowsAndMessaging::WS_BORDER;
use windows::Win32::UI::WindowsAndMessaging::WS_CAPTION;
use windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
use windows::Win32::UI::WindowsAndMessaging::WS_CHILDWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_CLIPCHILDREN;
use windows::Win32::UI::WindowsAndMessaging::WS_CLIPSIBLINGS;
use windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
use windows::Win32::UI::WindowsAndMessaging::WS_DLGFRAME;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_ACCEPTFILES;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_APPWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_CLIENTEDGE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_COMPOSITED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_CONTEXTHELP;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_CONTROLPARENT;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_DLGMODALFRAME;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYOUTRTL;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LEFT;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LEFTSCROLLBAR;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_LTRREADING;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_MDICHILD;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOINHERITLAYOUT;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOPARENTNOTIFY;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_NOREDIRECTIONBITMAP;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_OVERLAPPEDWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_PALETTEWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_RIGHT;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_RIGHTSCROLLBAR;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_RTLREADING;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_STATICEDGE;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_TRANSPARENT;
use windows::Win32::UI::WindowsAndMessaging::WS_EX_WINDOWEDGE;
use windows::Win32::UI::WindowsAndMessaging::WS_GROUP;
use windows::Win32::UI::WindowsAndMessaging::WS_HSCROLL;
use windows::Win32::UI::WindowsAndMessaging::WS_ICONIC;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZE;
use windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZE;
use windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPED;
use windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use windows::Win32::UI::WindowsAndMessaging::WS_POPUPWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_SIZEBOX;
use windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
use windows::Win32::UI::WindowsAndMessaging::WS_TABSTOP;
use windows::Win32::UI::WindowsAndMessaging::WS_THICKFRAME;
use windows::Win32::UI::WindowsAndMessaging::WS_TILED;
use windows::Win32::UI::WindowsAndMessaging::WS_TILEDWINDOW;
use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
use windows::Win32::UI::WindowsAndMessaging::WS_VSCROLL;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_BORDER;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_CAPTION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_CHILD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_CHILDWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_CLIPCHILDREN;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_CLIPSIBLINGS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_DISABLED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_DLGFRAME;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_ACCEPTFILES;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_APPWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_CLIENTEDGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_COMPOSITED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_CONTEXTHELP;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_CONTROLPARENT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_DLGMODALFRAME;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYOUTRTL;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_LEFT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_LEFTSCROLLBAR;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_LTRREADING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_MDICHILD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_NOINHERITLAYOUT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_NOPARENTNOTIFY;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_NOREDIRECTIONBITMAP;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_OVERLAPPEDWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_PALETTEWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_RIGHT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_RIGHTSCROLLBAR;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_RTLREADING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_STATICEDGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_TOPMOST;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_TRANSPARENT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_EX_WINDOWEDGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_GROUP;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_HSCROLL;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_ICONIC;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_MAXIMIZEBOX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_MINIMIZEBOX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_POPUP;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_POPUPWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_SIZEBOX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_SYSMENU;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_TABSTOP;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_THICKFRAME;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_TILED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_TILEDWINDOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WS_VSCROLL;
// https://docs.microsoft.com/en-us/windows/win32/winmsg/window-styles
bitflags! {
#[derive(Default)]
pub struct GwlStyle: u32 {
const BORDER = WS_BORDER.0;
const CAPTION = WS_CAPTION.0;
const CHILD = WS_CHILD.0;
const CHILDWINDOW = WS_CHILDWINDOW.0;
const CLIPCHILDREN = WS_CLIPCHILDREN.0;
const CLIPSIBLINGS = WS_CLIPSIBLINGS.0;
const DISABLED = WS_DISABLED.0;
const DLGFRAME = WS_DLGFRAME.0;
const GROUP = WS_GROUP.0;
const HSCROLL = WS_HSCROLL.0;
const ICONIC = WS_ICONIC.0;
const MAXIMIZE = WS_MAXIMIZE.0;
const MAXIMIZEBOX = WS_MAXIMIZEBOX.0;
const MINIMIZE = WS_MINIMIZE.0;
const MINIMIZEBOX = WS_MINIMIZEBOX.0;
const OVERLAPPED = WS_OVERLAPPED.0;
const OVERLAPPEDWINDOW = WS_OVERLAPPEDWINDOW.0;
const POPUP = WS_POPUP.0;
const POPUPWINDOW = WS_POPUPWINDOW.0;
const SIZEBOX = WS_SIZEBOX.0;
const SYSMENU = WS_SYSMENU.0;
const TABSTOP = WS_TABSTOP.0;
const THICKFRAME = WS_THICKFRAME.0;
const TILED = WS_TILED.0;
const TILEDWINDOW = WS_TILEDWINDOW.0;
const VISIBLE = WS_VISIBLE.0;
const VSCROLL = WS_VSCROLL.0;
pub struct WindowStyle: u32 {
const BORDER = WS_BORDER;
const CAPTION = WS_CAPTION;
const CHILD = WS_CHILD;
const CHILDWINDOW = WS_CHILDWINDOW;
const CLIPCHILDREN = WS_CLIPCHILDREN;
const CLIPSIBLINGS = WS_CLIPSIBLINGS;
const DISABLED = WS_DISABLED;
const DLGFRAME = WS_DLGFRAME;
const GROUP = WS_GROUP;
const HSCROLL = WS_HSCROLL;
const ICONIC = WS_ICONIC;
const MAXIMIZE = WS_MAXIMIZE;
const MAXIMIZEBOX = WS_MAXIMIZEBOX;
const MINIMIZE = WS_MINIMIZE;
const MINIMIZEBOX = WS_MINIMIZEBOX;
const OVERLAPPED = WS_OVERLAPPED;
const OVERLAPPEDWINDOW = WS_OVERLAPPEDWINDOW;
const POPUP = WS_POPUP;
const POPUPWINDOW = WS_POPUPWINDOW;
const SIZEBOX = WS_SIZEBOX;
const SYSMENU = WS_SYSMENU;
const TABSTOP = WS_TABSTOP;
const THICKFRAME = WS_THICKFRAME;
const TILED = WS_TILED;
const TILEDWINDOW = WS_TILEDWINDOW;
const VISIBLE = WS_VISIBLE;
const VSCROLL = WS_VSCROLL;
}
}
// https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
bitflags! {
#[derive(Default)]
pub struct GwlExStyle: u32 {
const ACCEPTFILES = WS_EX_ACCEPTFILES.0;
const APPWINDOW = WS_EX_APPWINDOW.0;
const CLIENTEDGE = WS_EX_CLIENTEDGE.0;
const COMPOSITED = WS_EX_COMPOSITED.0;
const CONTEXTHELP = WS_EX_CONTEXTHELP.0;
const CONTROLPARENT = WS_EX_CONTROLPARENT.0;
const DLGMODALFRAME = WS_EX_DLGMODALFRAME.0;
const LAYERED = WS_EX_LAYERED.0;
const LAYOUTRTL = WS_EX_LAYOUTRTL.0;
const LEFT = WS_EX_LEFT.0;
const LEFTSCROLLBAR = WS_EX_LEFTSCROLLBAR.0;
const LTRREADING = WS_EX_LTRREADING.0;
const MDICHILD = WS_EX_MDICHILD.0;
const NOACTIVATE = WS_EX_NOACTIVATE.0;
const NOINHERITLAYOUT = WS_EX_NOINHERITLAYOUT.0;
const NOPARENTNOTIFY = WS_EX_NOPARENTNOTIFY.0;
const NOREDIRECTIONBITMAP = WS_EX_NOREDIRECTIONBITMAP.0;
const OVERLAPPEDWINDOW = WS_EX_OVERLAPPEDWINDOW.0;
const PALETTEWINDOW = WS_EX_PALETTEWINDOW.0;
const RIGHT = WS_EX_RIGHT.0;
const RIGHTSCROLLBAR = WS_EX_RIGHTSCROLLBAR.0;
const RTLREADING = WS_EX_RTLREADING.0;
const STATICEDGE = WS_EX_STATICEDGE.0;
const TOOLWINDOW = WS_EX_TOOLWINDOW.0;
const TOPMOST = WS_EX_TOPMOST.0;
const TRANSPARENT = WS_EX_TRANSPARENT.0;
const WINDOWEDGE = WS_EX_WINDOWEDGE.0;
pub struct ExtendedWindowStyle: u32 {
const ACCEPTFILES = WS_EX_ACCEPTFILES;
const APPWINDOW = WS_EX_APPWINDOW;
const CLIENTEDGE = WS_EX_CLIENTEDGE;
const COMPOSITED = WS_EX_COMPOSITED;
const CONTEXTHELP = WS_EX_CONTEXTHELP;
const CONTROLPARENT = WS_EX_CONTROLPARENT;
const DLGMODALFRAME = WS_EX_DLGMODALFRAME;
const LAYERED = WS_EX_LAYERED;
const LAYOUTRTL = WS_EX_LAYOUTRTL;
const LEFT = WS_EX_LEFT;
const LEFTSCROLLBAR = WS_EX_LEFTSCROLLBAR;
const LTRREADING = WS_EX_LTRREADING;
const MDICHILD = WS_EX_MDICHILD;
const NOACTIVATE = WS_EX_NOACTIVATE;
const NOINHERITLAYOUT = WS_EX_NOINHERITLAYOUT;
const NOPARENTNOTIFY = WS_EX_NOPARENTNOTIFY;
const NOREDIRECTIONBITMAP = WS_EX_NOREDIRECTIONBITMAP;
const OVERLAPPEDWINDOW = WS_EX_OVERLAPPEDWINDOW;
const PALETTEWINDOW = WS_EX_PALETTEWINDOW;
const RIGHT = WS_EX_RIGHT;
const RIGHTSCROLLBAR = WS_EX_RIGHTSCROLLBAR;
const RTLREADING = WS_EX_RTLREADING;
const STATICEDGE = WS_EX_STATICEDGE;
const TOOLWINDOW = WS_EX_TOOLWINDOW;
const TOPMOST = WS_EX_TOPMOST;
const TRANSPARENT = WS_EX_TRANSPARENT;
const WINDOWEDGE = WS_EX_WINDOWEDGE;
}
}

View File

@@ -8,20 +8,21 @@ use serde::ser::Error;
use serde::ser::SerializeStruct;
use serde::Serialize;
use serde::Serializer;
use windows::Win32::Foundation::HWND;
use bindings::Windows::Win32::Foundation::HWND;
use komorebi_core::HidingBehaviour;
use komorebi_core::Rect;
use crate::styles::GwlExStyle;
use crate::styles::GwlStyle;
use crate::styles::ExtendedWindowStyle;
use crate::styles::WindowStyle;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_api::WindowsApi;
use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::FLOAT_IDENTIFIERS;
use crate::HIDDEN_HWNDS;
use crate::HIDING_BEHAVIOUR;
use crate::LAYERED_EXE_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::NO_TITLEBAR;
use crate::WSL2_UI_PROCESSES;
#[derive(Debug, Clone, Copy)]
@@ -140,7 +141,11 @@ impl Window {
programmatically_hidden_hwnds.push(self.hwnd);
}
WindowsApi::hide_window(self.hwnd());
let hiding_behaviour = HIDING_BEHAVIOUR.lock();
match *hiding_behaviour {
HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd()),
HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd()),
}
}
pub fn restore(self) {
@@ -188,7 +193,7 @@ impl Window {
WindowsApi::set_focus(self.hwnd())
}
pub fn focus(self) -> Result<()> {
pub fn focus(self, mouse_follows_focus: bool) -> Result<()> {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
let current_thread_id = WindowsApi::current_thread_id();
@@ -206,39 +211,27 @@ impl Window {
};
// Center cursor in Window
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd())?)?;
if mouse_follows_focus {
WindowsApi::center_cursor_in_rect(&WindowsApi::window_rect(self.hwnd())?)?;
}
// This isn't really needed when the above command works as expected via AHK
WindowsApi::set_focus(self.hwnd())
}
pub fn remove_title_bar(self) -> Result<()> {
let mut style = self.style()?;
style.remove(GwlStyle::CAPTION);
style.remove(GwlStyle::THICKFRAME);
self.update_style(style)
}
pub fn add_title_bar(self) -> Result<()> {
let mut style = self.style()?;
style.insert(GwlStyle::CAPTION);
style.insert(GwlStyle::THICKFRAME);
self.update_style(style)
}
#[allow(dead_code)]
pub fn update_style(self, style: GwlStyle) -> Result<()> {
pub fn update_style(self, style: WindowStyle) -> Result<()> {
WindowsApi::update_style(self.hwnd(), isize::try_from(style.bits())?)
}
pub fn style(self) -> Result<GwlStyle> {
pub fn style(self) -> Result<WindowStyle> {
let bits = u32::try_from(WindowsApi::gwl_style(self.hwnd())?)?;
GwlStyle::from_bits(bits).ok_or_else(|| anyhow!("there is no gwl style"))
WindowStyle::from_bits(bits).ok_or_else(|| anyhow!("there is no gwl style"))
}
pub fn ex_style(self) -> Result<GwlExStyle> {
pub fn ex_style(self) -> Result<ExtendedWindowStyle> {
let bits = u32::try_from(WindowsApi::gwl_ex_style(self.hwnd())?)?;
GwlExStyle::from_bits(bits).ok_or_else(|| anyhow!("there is no gwl style"))
ExtendedWindowStyle::from_bits(bits).ok_or_else(|| anyhow!("there is no gwl style"))
}
pub fn title(self) -> Result<String> {
@@ -310,25 +303,16 @@ impl Window {
wsl2_ui_processes.contains(&exe_name)
};
let allow_titlebar_removed = {
let titlebars_removed = NO_TITLEBAR.lock();
titlebars_removed.contains(&exe_name)
};
let style = self.style()?;
let ex_style = self.ex_style()?;
if (
allow_wsl2_gui
|| allow_titlebar_removed
|| style.contains(GwlStyle::CAPTION) && ex_style.contains(GwlExStyle::WINDOWEDGE)
)
&& !ex_style.contains(GwlExStyle::DLGMODALFRAME)
if (allow_wsl2_gui || style.contains(WindowStyle::CAPTION) && ex_style.contains(ExtendedWindowStyle::WINDOWEDGE))
&& !ex_style.contains(ExtendedWindowStyle::DLGMODALFRAME)
// Get a lot of dupe events coming through that make the redrawing go crazy
// on FocusChange events if I don't filter out this one. But, if we are
// allowing a specific layered window on the whitelist (like Steam), it should
// pass this check
&& (allow_layered || !ex_style.contains(GwlExStyle::LAYERED))
&& (allow_layered || !ex_style.contains(ExtendedWindowStyle::LAYERED))
|| managed_override
{
return Ok(true);

View File

@@ -2,7 +2,6 @@ use std::collections::VecDeque;
use std::io::ErrorKind;
use std::num::NonZeroUsize;
use std::path::PathBuf;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::thread;
@@ -17,16 +16,18 @@ use uds_windows::UnixListener;
use komorebi_core::custom_layout::CustomLayout;
use komorebi_core::Arrangement;
use komorebi_core::Axis;
use komorebi_core::CycleDirection;
use komorebi_core::DefaultLayout;
use komorebi_core::Flip;
use komorebi_core::FocusFollowsMouseImplementation;
use komorebi_core::Layout;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
use komorebi_core::Sizing;
use komorebi_core::WindowContainerBehaviour;
use crate::container::Container;
use crate::current_virtual_desktop;
use crate::load_configuration;
use crate::monitor::Monitor;
use crate::ring::Ring;
@@ -39,8 +40,6 @@ use crate::BORDER_OVERFLOW_IDENTIFIERS;
use crate::FLOAT_IDENTIFIERS;
use crate::LAYERED_EXE_WHITELIST;
use crate::MANAGE_IDENTIFIERS;
use crate::NO_TITLEBAR;
use crate::REMOVE_TITLEBARS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WORKSPACE_RULES;
@@ -52,10 +51,14 @@ pub struct WindowManager {
pub is_paused: bool,
pub invisible_borders: Rect,
pub work_area_offset: Option<Rect>,
pub resize_delta: i32,
pub window_container_behaviour: WindowContainerBehaviour,
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
pub mouse_follows_focus: bool,
pub hotwatch: Hotwatch,
pub virtual_desktop_id: Option<usize>,
pub virtual_desktop_id: Option<Vec<u8>>,
pub has_pending_raise_op: bool,
pub pending_move_op: Option<(usize, usize, usize)>,
}
#[derive(Debug, Serialize)]
@@ -63,10 +66,12 @@ pub struct State {
pub monitors: Ring<Monitor>,
pub is_paused: bool,
pub invisible_borders: Rect,
pub resize_delta: i32,
pub new_window_behaviour: WindowContainerBehaviour,
pub work_area_offset: Option<Rect>,
pub focus_follows_mouse: Option<FocusFollowsMouseImplementation>,
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_exe_whitelist: Vec<String>,
@@ -81,9 +86,11 @@ impl From<&WindowManager> for State {
is_paused: wm.is_paused,
invisible_borders: wm.invisible_borders,
work_area_offset: wm.work_area_offset,
resize_delta: wm.resize_delta,
new_window_behaviour: wm.window_container_behaviour,
focus_follows_mouse: wm.focus_follows_mouse.clone(),
mouse_follows_focus: wm.mouse_follows_focus,
has_pending_raise_op: wm.has_pending_raise_op,
remove_titlebars: REMOVE_TITLEBARS.load(Ordering::SeqCst),
float_identifiers: FLOAT_IDENTIFIERS.lock().clone(),
manage_identifiers: MANAGE_IDENTIFIERS.lock().clone(),
layered_exe_whitelist: LAYERED_EXE_WHITELIST.lock().clone(),
@@ -140,8 +147,6 @@ impl WindowManager {
let listener = UnixListener::bind(&socket)?;
let virtual_desktop_id = winvd::helpers::get_current_desktop_number().ok();
Ok(Self {
monitors: Ring::default(),
incoming_events: incoming,
@@ -153,11 +158,15 @@ impl WindowManager {
right: 14,
bottom: 7,
},
virtual_desktop_id: current_virtual_desktop(),
work_area_offset: None,
window_container_behaviour: WindowContainerBehaviour::Create,
resize_delta: 50,
focus_follows_mouse: None,
mouse_follows_focus: true,
hotwatch: Hotwatch::new()?,
virtual_desktop_id,
has_pending_raise_op: false,
pending_move_op: None,
})
}
@@ -433,7 +442,7 @@ impl WindowManager {
}
#[tracing::instrument(skip(self))]
pub fn retile_all(&mut self) -> Result<()> {
pub fn retile_all(&mut self, preserve_resize_dimensions: bool) -> Result<()> {
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
@@ -444,8 +453,10 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no workspace"))?;
// Reset any resize adjustments if we want to force a retile
for resize in workspace.resize_dimensions_mut() {
*resize = None;
if !preserve_resize_dimensions {
for resize in workspace.resize_dimensions_mut() {
*resize = None;
}
}
workspace.update(&work_area, offset, &invisible_borders)?;
@@ -454,20 +465,6 @@ impl WindowManager {
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn validate_virtual_desktop_id(&self) {
let virtual_desktop_id = winvd::helpers::get_current_desktop_number().ok();
if let (Some(id), Some(virtual_desktop_id)) = (virtual_desktop_id, self.virtual_desktop_id)
{
if id != virtual_desktop_id {
tracing::warn!(
"ignoring events while not on virtual desktop {}",
virtual_desktop_id
);
}
}
}
#[tracing::instrument(skip(self))]
pub fn manage_focused_window(&mut self) -> Result<()> {
let hwnd = WindowsApi::foreground_window()?;
@@ -544,7 +541,94 @@ impl WindowManager {
}
#[tracing::instrument(skip(self))]
pub fn update_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> {
pub fn transfer_container(
&mut self,
origin: (usize, usize, usize),
target: (usize, usize, usize),
) -> Result<()> {
let (origin_monitor_idx, origin_workspace_idx, origin_container_idx) = origin;
let (target_monitor_idx, target_workspace_idx, target_container_idx) = target;
let origin_container = self
.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(origin_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?
.remove_container(origin_container_idx)
.ok_or_else(|| anyhow!("there is no container at this index"))?;
let target_workspace = self
.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(target_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?;
target_workspace
.containers_mut()
.insert(target_container_idx, origin_container);
target_workspace.focus_container(target_container_idx);
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn swap_containers(
&mut self,
origin: (usize, usize, usize),
target: (usize, usize, usize),
) -> Result<()> {
let (origin_monitor_idx, origin_workspace_idx, origin_container_idx) = origin;
let (target_monitor_idx, target_workspace_idx, target_container_idx) = target;
let origin_container = self
.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(origin_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?
.remove_container(origin_container_idx)
.ok_or_else(|| anyhow!("there is no container at this index"))?;
let target_container = self
.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(target_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?
.remove_container(target_container_idx);
self.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(target_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?
.containers_mut()
.insert(target_container_idx, origin_container);
if let Some(target_container) = target_container {
self.monitors_mut()
.get_mut(origin_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this index"))?
.workspaces_mut()
.get_mut(origin_workspace_idx)
.ok_or_else(|| anyhow!("there is no workspace at this index"))?
.containers_mut()
.insert(origin_container_idx, target_container);
}
Ok(())
}
#[tracing::instrument(skip(self))]
pub fn update_focused_workspace(&mut self, follow_focus: bool) -> Result<()> {
tracing::info!("updating");
let invisible_borders = self.invisible_borders;
@@ -554,15 +638,15 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no monitor"))?
.update_focused_workspace(offset, &invisible_borders)?;
if mouse_follows_focus {
if follow_focus {
if let Some(window) = self.focused_workspace()?.maximized_window() {
window.focus()?;
window.focus(self.mouse_follows_focus)?;
} else if let Some(container) = self.focused_workspace()?.monocle_container() {
if let Some(window) = container.focused_window() {
window.focus()?;
window.focus(self.mouse_follows_focus)?;
}
} else if let Ok(window) = self.focused_window_mut() {
window.focus()?;
window.focus(self.mouse_follows_focus)?;
} else {
let desktop_window = Window {
hwnd: WindowsApi::desktop_window()?,
@@ -588,7 +672,8 @@ impl WindowManager {
&mut self,
direction: OperationDirection,
sizing: Sizing,
step: Option<i32>,
delta: i32,
update: bool,
) -> Result<()> {
let work_area = self.focused_monitor_work_area()?;
let workspace = self.focused_workspace_mut()?;
@@ -627,21 +712,21 @@ impl WindowManager {
// can flip them however they need to be flipped once the resizing has been done
if let Some(flip) = workspace.layout_flip() {
match flip {
Flip::Horizontal => {
Axis::Horizontal => {
if matches!(direction, OperationDirection::Left)
|| matches!(direction, OperationDirection::Right)
{
direction = direction.opposite();
}
}
Flip::Vertical => {
Axis::Vertical => {
if matches!(direction, OperationDirection::Up)
|| matches!(direction, OperationDirection::Down)
{
direction = direction.opposite();
}
}
Flip::HorizontalAndVertical => direction = direction.opposite(),
Axis::HorizontalAndVertical => direction = direction.opposite(),
}
}
@@ -652,11 +737,16 @@ impl WindowManager {
focused_idx_resize,
direction,
sizing,
step,
delta,
);
workspace.resize_dimensions_mut()[focused_idx] = resize;
return self.update_focused_workspace(false);
return if update {
self.update_focused_workspace(false)
} else {
Ok(())
};
}
tracing::warn!("cannot resize container in this direction");
@@ -669,26 +759,18 @@ impl WindowManager {
}
#[tracing::instrument(skip(self))]
pub fn restore_all_windows(&mut self) -> Result<()> {
pub fn restore_all_windows(&mut self) {
tracing::info!("restoring all hidden windows");
let no_titlebar = NO_TITLEBAR.lock();
for monitor in self.monitors_mut() {
for workspace in monitor.workspaces_mut() {
for containers in workspace.containers_mut() {
for window in containers.windows_mut() {
if no_titlebar.contains(&window.exe()?) {
window.add_title_bar()?;
}
window.restore();
}
}
}
}
Ok(())
}
#[tracing::instrument(skip(self))]
@@ -697,6 +779,7 @@ impl WindowManager {
let invisible_borders = self.invisible_borders;
let offset = self.work_area_offset;
let mouse_follows_focus = self.mouse_follows_focus;
let monitor = self
.focused_monitor_mut()
@@ -721,28 +804,57 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no monitor"))?;
target_monitor.add_container(container)?;
target_monitor.load_focused_workspace()?;
target_monitor.load_focused_workspace(mouse_follows_focus)?;
target_monitor.update_focused_workspace(offset, &invisible_borders)?;
if follow {
self.focus_monitor(idx)?;
}
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn move_container_to_workspace(&mut self, idx: usize, follow: bool) -> Result<()> {
tracing::info!("moving container");
let mouse_follows_focus = self.mouse_follows_focus;
let monitor = self
.focused_monitor_mut()
.ok_or_else(|| anyhow!("there is no monitor"))?;
monitor.move_container_to_workspace(idx, follow)?;
monitor.load_focused_workspace()?;
monitor.load_focused_workspace(mouse_follows_focus)?;
self.update_focused_workspace(true)
self.update_focused_workspace(mouse_follows_focus)
}
pub fn remove_focused_workspace(&mut self) -> Option<Workspace> {
let focused_monitor: &mut Monitor = self.focused_monitor_mut()?;
let focused_workspace_idx = focused_monitor.focused_workspace_idx();
focused_monitor.remove_workspace_by_idx(focused_workspace_idx)
}
#[tracing::instrument(skip(self))]
pub fn move_workspace_to_monitor(&mut self, idx: usize) -> Result<()> {
tracing::info!("moving workspace");
let mouse_follows_focus = self.mouse_follows_focus;
let workspace = self
.remove_focused_workspace()
.ok_or_else(|| anyhow!("there is no workspace"))?;
{
let target_monitor: &mut Monitor = self
.monitors_mut()
.get_mut(idx)
.ok_or_else(|| anyhow!("there is no monitor"))?;
target_monitor.workspaces_mut().push_back(workspace);
target_monitor.focus_workspace(target_monitor.workspaces().len() - 1)?;
target_monitor.load_focused_workspace(mouse_follows_focus)?;
}
self.focus_monitor(idx)?;
self.update_focused_workspace(mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -755,7 +867,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
workspace.focus_container(new_idx);
self.focused_window_mut()?.focus()?;
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;
Ok(())
}
@@ -773,7 +885,7 @@ impl WindowManager {
workspace.swap_containers(current_idx, new_idx);
workspace.focus_container(new_idx);
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -786,7 +898,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("this is not a valid direction from the current position"))?;
workspace.focus_container(new_idx);
self.focused_window_mut()?.focus()?;
self.focused_window_mut()?.focus(self.mouse_follows_focus)?;
Ok(())
}
@@ -804,7 +916,7 @@ impl WindowManager {
workspace.swap_containers(current_idx, new_idx);
workspace.focus_container(new_idx);
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -826,7 +938,7 @@ impl WindowManager {
container.focus_window(next_idx);
container.load_focused_window();
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -859,7 +971,7 @@ impl WindowManager {
};
workspace.move_window_to_container(adjusted_new_index)?;
self.update_focused_workspace(true)?;
self.update_focused_workspace(self.mouse_follows_focus)?;
}
Ok(())
@@ -871,7 +983,7 @@ impl WindowManager {
let workspace = self.focused_workspace_mut()?;
workspace.promote_container()?;
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -885,7 +997,7 @@ impl WindowManager {
let workspace = self.focused_workspace_mut()?;
workspace.new_container_for_focused_window()?;
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -933,7 +1045,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no floating window"))?;
window.center(&work_area, &invisible_borders)?;
window.focus()?;
window.focus(self.mouse_follows_focus)?;
Ok(())
}
@@ -1003,7 +1115,7 @@ impl WindowManager {
}
#[tracing::instrument(skip(self))]
pub fn flip_layout(&mut self, layout_flip: Flip) -> Result<()> {
pub fn flip_layout(&mut self, layout_flip: Axis) -> Result<()> {
tracing::info!("flipping layout");
let workspace = self.focused_workspace_mut()?;
@@ -1015,28 +1127,28 @@ impl WindowManager {
}
Some(current_layout_flip) => {
match current_layout_flip {
Flip::Horizontal => match layout_flip {
Flip::Horizontal => workspace.set_layout_flip(None),
Flip::Vertical => {
workspace.set_layout_flip(Option::from(Flip::HorizontalAndVertical))
Axis::Horizontal => match layout_flip {
Axis::Horizontal => workspace.set_layout_flip(None),
Axis::Vertical => {
workspace.set_layout_flip(Option::from(Axis::HorizontalAndVertical))
}
Flip::HorizontalAndVertical => {
workspace.set_layout_flip(Option::from(Flip::HorizontalAndVertical))
Axis::HorizontalAndVertical => {
workspace.set_layout_flip(Option::from(Axis::HorizontalAndVertical))
}
},
Flip::Vertical => match layout_flip {
Flip::Horizontal => {
workspace.set_layout_flip(Option::from(Flip::HorizontalAndVertical))
Axis::Vertical => match layout_flip {
Axis::Horizontal => {
workspace.set_layout_flip(Option::from(Axis::HorizontalAndVertical))
}
Flip::Vertical => workspace.set_layout_flip(None),
Flip::HorizontalAndVertical => {
workspace.set_layout_flip(Option::from(Flip::HorizontalAndVertical))
Axis::Vertical => workspace.set_layout_flip(None),
Axis::HorizontalAndVertical => {
workspace.set_layout_flip(Option::from(Axis::HorizontalAndVertical))
}
},
Flip::HorizontalAndVertical => match layout_flip {
Flip::Horizontal => workspace.set_layout_flip(Option::from(Flip::Vertical)),
Flip::Vertical => workspace.set_layout_flip(Option::from(Flip::Horizontal)),
Flip::HorizontalAndVertical => workspace.set_layout_flip(None),
Axis::HorizontalAndVertical => match layout_flip {
Axis::Horizontal => workspace.set_layout_flip(Option::from(Axis::Vertical)),
Axis::Vertical => workspace.set_layout_flip(Option::from(Axis::Horizontal)),
Axis::HorizontalAndVertical => workspace.set_layout_flip(None),
},
};
}
@@ -1067,7 +1179,7 @@ impl WindowManager {
}
workspace.set_layout(Layout::Default(layout));
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -1093,7 +1205,7 @@ impl WindowManager {
}
workspace.set_layout(Layout::Custom(layout));
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
@@ -1375,28 +1487,30 @@ impl WindowManager {
pub fn focus_workspace(&mut self, idx: usize) -> Result<()> {
tracing::info!("focusing workspace");
let mouse_follows_focus = self.mouse_follows_focus;
let monitor = self
.focused_monitor_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?;
monitor.focus_workspace(idx)?;
monitor.load_focused_workspace()?;
monitor.load_focused_workspace(mouse_follows_focus)?;
self.update_focused_workspace(true)
self.update_focused_workspace(mouse_follows_focus)
}
#[tracing::instrument(skip(self))]
pub fn new_workspace(&mut self) -> Result<()> {
tracing::info!("adding new workspace");
let mouse_follows_focus = self.mouse_follows_focus;
let monitor = self
.focused_monitor_mut()
.ok_or_else(|| anyhow!("there is no workspace"))?;
monitor.focus_workspace(monitor.new_workspace_idx())?;
monitor.load_focused_workspace()?;
monitor.load_focused_workspace(mouse_follows_focus)?;
self.update_focused_workspace(true)
self.update_focused_workspace(self.mouse_follows_focus)
}
pub fn focused_container(&self) -> Result<&Container> {

View File

@@ -15,6 +15,7 @@ pub enum WindowManagerEvent {
Hide(WinEvent, Window),
Minimize(WinEvent, Window),
Show(WinEvent, Window),
MoveResizeStart(WinEvent, Window),
MoveResizeEnd(WinEvent, Window),
MouseCapture(WinEvent, Window),
Manage(Window),
@@ -51,6 +52,13 @@ impl Display for WindowManagerEvent {
WindowManagerEvent::Show(winevent, window) => {
write!(f, "Show (WinEvent: {}, Window: {})", winevent, window)
}
WindowManagerEvent::MoveResizeStart(winevent, window) => {
write!(
f,
"MoveResizeStart (WinEvent: {}, Window: {})",
winevent, window
)
}
WindowManagerEvent::MoveResizeEnd(winevent, window) => {
write!(
f,
@@ -87,6 +95,7 @@ impl WindowManagerEvent {
| WindowManagerEvent::Hide(_, window)
| WindowManagerEvent::Minimize(_, window)
| WindowManagerEvent::Show(_, window)
| WindowManagerEvent::MoveResizeStart(_, window)
| WindowManagerEvent::MoveResizeEnd(_, window)
| WindowManagerEvent::MouseCapture(_, window)
| WindowManagerEvent::MonitorPoll(_, window)
@@ -113,6 +122,7 @@ impl WindowManagerEvent {
WinEvent::ObjectFocus | WinEvent::SystemForeground => {
Option::from(Self::FocusChange(winevent, window))
}
WinEvent::SystemMoveSizeStart => Option::from(Self::MoveResizeStart(winevent, window)),
WinEvent::SystemMoveSizeEnd => Option::from(Self::MoveResizeEnd(winevent, window)),
WinEvent::SystemCaptureStart | WinEvent::SystemCaptureEnd => {
Option::from(Self::MouseCapture(winevent, window))

View File

@@ -6,80 +6,79 @@ use std::ffi::c_void;
use color_eyre::eyre::anyhow;
use color_eyre::eyre::Error;
use color_eyre::Result;
use windows::core::Result as WindowsCrateResult;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::POINT;
use windows::Win32::Foundation::PWSTR;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
use windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
use windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
use windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
use windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
use windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use windows::Win32::Graphics::Gdi::MonitorFromPoint;
use windows::Win32::Graphics::Gdi::MonitorFromWindow;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::Graphics::Gdi::MONITORENUMPROC;
use windows::Win32::Graphics::Gdi::MONITORINFO;
use windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use windows::Win32::System::RemoteDesktop::ProcessIdToSessionId;
use windows::Win32::System::Threading::AttachThreadInput;
use windows::Win32::System::Threading::GetCurrentProcessId;
use windows::Win32::System::Threading::GetCurrentThreadId;
use windows::Win32::System::Threading::OpenProcess;
use windows::Win32::System::Threading::QueryFullProcessImageNameW;
use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use windows::Win32::UI::Input::KeyboardAndMouse::SetFocus;
use windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::GetTopWindow;
use windows::Win32::UI::WindowsAndMessaging::GetWindow;
use windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
use windows::Win32::UI::WindowsAndMessaging::GetWindowTextW;
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use windows::Win32::UI::WindowsAndMessaging::IsIconic;
use windows::Win32::UI::WindowsAndMessaging::IsWindow;
use windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
use windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use windows::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_MINIMIZE;
use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use bindings::Handle;
use bindings::Result as WindowsCrateResult;
use bindings::Windows::Win32::Foundation::BOOL;
use bindings::Windows::Win32::Foundation::HANDLE;
use bindings::Windows::Win32::Foundation::HWND;
use bindings::Windows::Win32::Foundation::LPARAM;
use bindings::Windows::Win32::Foundation::POINT;
use bindings::Windows::Win32::Foundation::PWSTR;
use bindings::Windows::Win32::Foundation::RECT;
use bindings::Windows::Win32::Graphics::Dwm::DwmGetWindowAttribute;
use bindings::Windows::Win32::Graphics::Dwm::DWMWA_CLOAKED;
use bindings::Windows::Win32::Graphics::Dwm::DWMWA_EXTENDED_FRAME_BOUNDS;
use bindings::Windows::Win32::Graphics::Dwm::DWMWINDOWATTRIBUTE;
use bindings::Windows::Win32::Graphics::Dwm::DWM_CLOAKED_APP;
use bindings::Windows::Win32::Graphics::Dwm::DWM_CLOAKED_INHERITED;
use bindings::Windows::Win32::Graphics::Dwm::DWM_CLOAKED_SHELL;
use bindings::Windows::Win32::Graphics::Gdi::EnumDisplayMonitors;
use bindings::Windows::Win32::Graphics::Gdi::GetMonitorInfoW;
use bindings::Windows::Win32::Graphics::Gdi::MonitorFromPoint;
use bindings::Windows::Win32::Graphics::Gdi::MonitorFromWindow;
use bindings::Windows::Win32::Graphics::Gdi::HDC;
use bindings::Windows::Win32::Graphics::Gdi::HMONITOR;
use bindings::Windows::Win32::Graphics::Gdi::MONITORENUMPROC;
use bindings::Windows::Win32::Graphics::Gdi::MONITORINFO;
use bindings::Windows::Win32::Graphics::Gdi::MONITOR_DEFAULTTONEAREST;
use bindings::Windows::Win32::System::Threading::AttachThreadInput;
use bindings::Windows::Win32::System::Threading::GetCurrentProcessId;
use bindings::Windows::Win32::System::Threading::GetCurrentThreadId;
use bindings::Windows::Win32::System::Threading::OpenProcess;
use bindings::Windows::Win32::System::Threading::QueryFullProcessImageNameW;
use bindings::Windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
use bindings::Windows::Win32::System::Threading::PROCESS_NAME_FORMAT;
use bindings::Windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
use bindings::Windows::Win32::UI::KeyboardAndMouseInput::SetFocus;
use bindings::Windows::Win32::UI::WindowsAndMessaging::AllowSetForegroundWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EnumWindows;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetCursorPos;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetDesktopWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetTopWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindowLongPtrW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindowRect;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindowTextW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
use bindings::Windows::Win32::UI::WindowsAndMessaging::IsIconic;
use bindings::Windows::Win32::UI::WindowsAndMessaging::IsWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::IsWindowVisible;
use bindings::Windows::Win32::UI::WindowsAndMessaging::RealGetWindowClassW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetForegroundWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowLongPtrW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SetWindowPos;
use bindings::Windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SystemParametersInfoW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WindowFromPoint;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_EXSTYLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GWL_STYLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::GW_HWNDNEXT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::HWND_NOTOPMOST;
use bindings::Windows::Win32::UI::WindowsAndMessaging::HWND_TOPMOST;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SET_WINDOW_POS_FLAGS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPI_GETACTIVEWINDOWTRACKING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::WNDENUMPROC;
use komorebi_core::Rect;
use crate::container::Container;
@@ -165,7 +164,7 @@ impl WindowsApi {
EnumDisplayMonitors(
HDC(0),
std::ptr::null_mut(),
Option::from(callback),
callback,
LPARAM(callback_data_address),
)
}
@@ -177,7 +176,7 @@ impl WindowsApi {
let mut monitors: Vec<isize> = vec![];
let monitors_ref: &mut Vec<isize> = monitors.as_mut();
Self::enum_display_monitors(
windows_callbacks::valid_display_monitors,
Option::Some(windows_callbacks::valid_display_monitors),
monitors_ref as *mut Vec<isize> as isize,
)?;
@@ -186,13 +185,13 @@ impl WindowsApi {
pub fn load_monitor_information(monitors: &mut Ring<Monitor>) -> Result<()> {
Self::enum_display_monitors(
windows_callbacks::enum_display_monitor,
Option::Some(windows_callbacks::enum_display_monitor),
monitors as *mut Ring<Monitor> as isize,
)
}
pub fn enum_windows(callback: WNDENUMPROC, callback_data_address: isize) -> Result<()> {
unsafe { EnumWindows(Option::from(callback), LPARAM(callback_data_address)) }
unsafe { EnumWindows(callback, LPARAM(callback_data_address)) }
.ok()
.process()
}
@@ -203,7 +202,7 @@ impl WindowsApi {
if let Some(workspace) = monitor.workspaces_mut().front_mut() {
// EnumWindows will enumerate through windows on all monitors
Self::enum_windows(
windows_callbacks::enum_window,
Option::Some(windows_callbacks::enum_window),
workspace.containers_mut() as *mut VecDeque<Container> as isize,
)?;
@@ -266,7 +265,7 @@ impl WindowsApi {
layout.top,
layout.right,
layout.bottom,
SET_WINDOW_POS_FLAGS(flags),
flags,
)
}
.ok()
@@ -279,6 +278,10 @@ impl WindowsApi {
unsafe { ShowWindow(hwnd, command) };
}
pub fn minimize_window(hwnd: HWND) {
Self::show_window(hwnd, SW_MINIMIZE);
}
pub fn hide_window(hwnd: HWND) {
Self::show_window(hwnd, SW_HIDE);
}
@@ -377,6 +380,19 @@ impl WindowsApi {
unsafe { GetCurrentProcessId() }
}
pub fn process_id_to_session_id() -> Result<u32> {
let process_id = Self::current_process_id();
let mut session_id = 0;
unsafe {
if ProcessIdToSessionId(process_id, &mut session_id).as_bool() {
Ok(session_id)
} else {
Err(anyhow!("could not determine current session id"))
}
}
}
pub fn attach_thread_input(thread_id: u32, target_thread_id: u32, attach: bool) -> Result<()> {
unsafe { AttachThreadInput(thread_id, target_thread_id, attach) }
.ok()
@@ -408,9 +424,9 @@ impl WindowsApi {
}
fn window_long_ptr_w(hwnd: HWND, index: WINDOW_LONG_PTR_INDEX) -> Result<isize> {
// Can return 0, which does not always mean that an error has occurred
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowlongptrw
Result::from(unsafe { WindowsResult::Ok(GetWindowLongPtrW(hwnd, index)) })
Result::from(WindowsResult::from(unsafe {
GetWindowLongPtrW(hwnd, index)
}))
}
#[allow(dead_code)]
@@ -451,12 +467,7 @@ impl WindowsApi {
let text_ptr = path.as_mut_ptr();
unsafe {
QueryFullProcessImageNameW(
handle,
PROCESS_NAME_FORMAT(0),
PWSTR(text_ptr),
&mut len as *mut u32,
)
QueryFullProcessImageNameW(handle, 0, PWSTR(text_ptr), std::ptr::addr_of_mut!(len))
}
.ok()
.process()?;
@@ -491,7 +502,7 @@ impl WindowsApi {
unsafe {
DwmGetWindowAttribute(
hwnd,
std::mem::transmute::<_, u32>(attribute),
attribute,
(value as *mut T).cast(),
u32::try_from(std::mem::size_of::<T>())?,
)?;
@@ -534,7 +545,7 @@ impl WindowsApi {
let mut monitor_info: MONITORINFO = unsafe { std::mem::zeroed() };
monitor_info.cbSize = u32::try_from(std::mem::size_of::<MONITORINFO>())?;
unsafe { GetMonitorInfoW(hmonitor, (&mut monitor_info as *mut MONITORINFO).cast()) }
unsafe { GetMonitorInfoW(hmonitor, std::ptr::addr_of_mut!(monitor_info).cast()) }
.ok()
.process()?;
@@ -570,7 +581,7 @@ impl WindowsApi {
Self::system_parameters_info_w(
SPI_GETACTIVEWINDOWTRACKING,
0,
(&mut is_enabled as *mut BOOL).cast(),
std::ptr::addr_of_mut!(is_enabled).cast(),
SPIF_SENDCHANGE,
)?;

View File

@@ -1,12 +1,12 @@
use std::collections::VecDeque;
use bindings::Windows::Win32::Foundation::BOOL;
use bindings::Windows::Win32::Foundation::HWND;
use bindings::Windows::Win32::Foundation::LPARAM;
use bindings::Windows::Win32::Foundation::RECT;
use bindings::Windows::Win32::Graphics::Gdi::HDC;
use bindings::Windows::Win32::Graphics::Gdi::HMONITOR;
use bindings::Windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use windows::Win32::Foundation::BOOL;
use windows::Win32::Foundation::HWND;
use windows::Win32::Foundation::LPARAM;
use windows::Win32::Foundation::RECT;
use windows::Win32::Graphics::Gdi::HDC;
use windows::Win32::Graphics::Gdi::HMONITOR;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
use crate::container::Container;
use crate::monitor::Monitor;

View File

@@ -1,90 +1,89 @@
use serde::Serialize;
use strum::Display;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_AIA_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_AIA_START;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_CARET;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_END_APPLICATION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_LAYOUT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_START_APPLICATION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_REGION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_SCROLL;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_SIMPLE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_ACCELERATORCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CLOAKED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CONTENTSCROLLED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CREATE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DEFACTIONCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESCRIPTIONCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESTROY;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGCANCEL;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGCOMPLETE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGDROPPED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGENTER;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGLEAVE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_FOCUS;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HELPCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HIDE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_CHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_HIDE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_SHOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_INVOKED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LIVEREGIONCHANGED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_NAMECHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_PARENTCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_REORDER;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONADD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONREMOVE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONWITHIN;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SHOW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_STATECHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_TEXTSELECTIONCHANGED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_UNCLOAKED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_VALUECHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OEM_DEFINED_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_OEM_DEFINED_START;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_ALERT;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_ARRANGMENTPREVIEW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CAPTUREEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CAPTURESTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CONTEXTHELPEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CONTEXTHELPSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DESKTOPSWITCH;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DIALOGEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DIALOGSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DRAGDROPEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DRAGDROPSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_FOREGROUND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_IME_KEY_NOTIFICATION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUPOPUPEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUPOPUPSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MINIMIZEEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MINIMIZESTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MOVESIZEEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MOVESIZESTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SCROLLINGEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SCROLLINGSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SOUND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHEND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPDROPPED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPGRABBED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPOVERTARGET;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_CANCELLED;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHSTART;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_EVENTID_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_EVENTID_START;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_END;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_START;
use windows::Win32::UI::WindowsAndMessaging::EVENT_AIA_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_AIA_START;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_CARET;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_END_APPLICATION;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_LAYOUT;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_START_APPLICATION;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_REGION;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_SCROLL;
use windows::Win32::UI::WindowsAndMessaging::EVENT_CONSOLE_UPDATE_SIMPLE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_ACCELERATORCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CLOAKED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CONTENTSCROLLED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_CREATE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DEFACTIONCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESCRIPTIONCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DESTROY;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGCANCEL;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGCOMPLETE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGDROPPED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGENTER;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGLEAVE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_DRAGSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_FOCUS;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HELPCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HIDE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_CHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_HIDE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_IME_SHOW;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_INVOKED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LIVEREGIONCHANGED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_LOCATIONCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_NAMECHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_PARENTCHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_REORDER;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTION;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONADD;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONREMOVE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SELECTIONWITHIN;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_SHOW;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_STATECHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_TEXTSELECTIONCHANGED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_UNCLOAKED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OBJECT_VALUECHANGE;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OEM_DEFINED_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_OEM_DEFINED_START;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_ALERT;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_ARRANGMENTPREVIEW;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CAPTUREEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CAPTURESTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CONTEXTHELPEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_CONTEXTHELPSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DESKTOPSWITCH;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DIALOGEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DIALOGSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DRAGDROPEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_DRAGDROPSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_FOREGROUND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_IME_KEY_NOTIFICATION;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUPOPUPEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUPOPUPSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MENUSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MINIMIZEEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MINIMIZESTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MOVESIZEEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_MOVESIZESTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SCROLLINGEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SCROLLINGSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SOUND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHEND;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPDROPPED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPGRABBED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_APPOVERTARGET;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHER_CANCELLED;
use windows::Win32::UI::WindowsAndMessaging::EVENT_SYSTEM_SWITCHSTART;
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_EVENTID_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_EVENTID_START;
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_END;
use windows::Win32::UI::WindowsAndMessaging::EVENT_UIA_PROPID_START;
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Display)]
#[repr(u32)]

View File

@@ -8,16 +8,15 @@ use crossbeam_channel::Receiver;
use crossbeam_channel::Sender;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use bindings::Windows::Win32::Foundation::HWND;
use bindings::Windows::Win32::UI::Accessibility::SetWinEventHook;
use bindings::Windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::PeekMessageW;
use bindings::Windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_MAX;
use bindings::Windows::Win32::UI::WindowsAndMessaging::EVENT_MIN;
use bindings::Windows::Win32::UI::WindowsAndMessaging::MSG;
use bindings::Windows::Win32::UI::WindowsAndMessaging::PM_REMOVE;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::Accessibility::SetWinEventHook;
use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW;
use windows::Win32::UI::WindowsAndMessaging::PeekMessageW;
use windows::Win32::UI::WindowsAndMessaging::TranslateMessage;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MAX;
use windows::Win32::UI::WindowsAndMessaging::EVENT_MIN;
use windows::Win32::UI::WindowsAndMessaging::MSG;
use windows::Win32::UI::WindowsAndMessaging::PM_REMOVE;
use crate::window_manager_event::WindowManagerEvent;
use crate::windows_callbacks;

View File

@@ -1,6 +1,5 @@
use std::collections::VecDeque;
use std::num::NonZeroUsize;
use std::sync::atomic::Ordering;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
@@ -10,9 +9,9 @@ use getset::MutGetters;
use getset::Setters;
use serde::Serialize;
use komorebi_core::Axis;
use komorebi_core::CycleDirection;
use komorebi_core::DefaultLayout;
use komorebi_core::Flip;
use komorebi_core::Layout;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
@@ -21,8 +20,6 @@ use crate::container::Container;
use crate::ring::Ring;
use crate::window::Window;
use crate::windows_api::WindowsApi;
use crate::NO_TITLEBAR;
use crate::REMOVE_TITLEBARS;
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters)]
pub struct Workspace {
@@ -41,10 +38,10 @@ pub struct Workspace {
maximized_window_restore_idx: Option<usize>,
#[getset(get = "pub", get_mut = "pub")]
floating_windows: Vec<Window>,
#[getset(get = "pub", set = "pub")]
#[getset(get = "pub", get_mut = "pub", set = "pub")]
layout: Layout,
#[getset(get_copy = "pub", set = "pub")]
layout_flip: Option<Flip>,
layout_flip: Option<Axis>,
#[getset(get_copy = "pub", set = "pub")]
workspace_padding: Option<i32>,
#[getset(get_copy = "pub", set = "pub")]
@@ -104,7 +101,7 @@ impl Workspace {
}
}
pub fn restore(&mut self) -> Result<()> {
pub fn restore(&mut self, mouse_follows_focus: bool) -> Result<()> {
let idx = self.focused_container_idx();
let mut to_focus = None;
for (i, container) in self.containers_mut().iter_mut().enumerate() {
@@ -135,7 +132,7 @@ impl Workspace {
// Maximised windows should always be drawn at the top of the Z order
if let Some(window) = to_focus {
if self.maximized_window().is_none() {
window.focus()?;
window.focus(mouse_follows_focus)?;
}
}
@@ -187,18 +184,9 @@ impl Workspace {
self.resize_dimensions(),
);
let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst);
let no_titlebar = { NO_TITLEBAR.lock().clone() };
let windows = self.visible_windows_mut();
for (i, window) in windows.into_iter().enumerate() {
if let (Some(window), Some(layout)) = (window, layouts.get(i)) {
if should_remove_titlebars && no_titlebar.contains(&window.exe()?) {
window.remove_title_bar()?;
} else if no_titlebar.contains(&window.exe()?) {
window.add_title_bar()?;
}
window.set_position(layout, invisible_borders, false)?;
}
}
@@ -468,9 +456,11 @@ impl Workspace {
if self.resize_dimensions().get(container_idx).is_some() {
self.resize_dimensions_mut().remove(container_idx);
}
}
self.focus_previous_container();
self.focus_previous_container();
} else {
container.load_focused_window();
}
Ok(())
}
@@ -483,6 +473,13 @@ impl Workspace {
container
}
pub fn remove_container(&mut self, idx: usize) -> Option<Container> {
let container = self.remove_container_by_idx(idx);
self.focus_previous_container();
container
}
pub fn new_idx_for_direction(&self, direction: OperationDirection) -> Option<usize> {
let len = NonZeroUsize::new(self.containers().len())?;

View File

@@ -28,20 +28,20 @@ Log() {
Run, komorebic.exe log, , Hide
}
QuickSave() {
Run, komorebic.exe quick-save, , Hide
QuickSaveResize() {
Run, komorebic.exe quick-save-resize, , Hide
}
QuickLoad() {
Run, komorebic.exe quick-load, , Hide
QuickLoadResize() {
Run, komorebic.exe quick-load-resize, , Hide
}
Save(path) {
Run, komorebic.exe save %path%, , Hide
SaveResize(path) {
Run, komorebic.exe save-resize %path%, , Hide
}
Load(path) {
Run, komorebic.exe load %path%, , Hide
LoadResize(path) {
Run, komorebic.exe load-resize %path%, , Hide
}
Focus(operation_direction) {
@@ -68,6 +68,10 @@ Resize(edge, sizing) {
Run, komorebic.exe resize %edge% %sizing%, , Hide
}
ResizeAxis(axis, sizing) {
Run, komorebic.exe resize-axis %axis% %sizing%, , Hide
}
Unstack() {
Run, komorebic.exe unstack, , Hide
}
@@ -100,6 +104,10 @@ FocusWorkspace(target) {
Run, komorebic.exe focus-workspace %target%, , Hide
}
FocusMonitorWorkspace(target_monitor, target_workspace) {
Run, komorebic.exe focus-monitor-workspace %target_monitor% %target_workspace%, , Hide
}
CycleMonitor(cycle_direction) {
Run, komorebic.exe cycle-monitor %cycle_direction%, , Hide
}
@@ -108,10 +116,18 @@ CycleWorkspace(cycle_direction) {
Run, komorebic.exe cycle-workspace %cycle_direction%, , Hide
}
MoveWorkspaceToMonitor(target) {
Run, komorebic.exe move-workspace-to-monitor %target%, , Hide
}
NewWorkspace() {
Run, komorebic.exe new-workspace, , Hide
}
ResizeDelta(pixels) {
Run, komorebic.exe resize-delta %pixels%, , Hide
}
InvisibleBorders(left, top, right, bottom) {
Run, komorebic.exe invisible-borders %left% %top% %right% %bottom%, , Hide
}
@@ -136,8 +152,8 @@ LoadCustomLayout(path) {
Run, komorebic.exe load-custom-layout %path%, , Hide
}
FlipLayout(flip) {
Run, komorebic.exe flip-layout %flip%, , Hide
FlipLayout(axis) {
Run, komorebic.exe flip-layout %axis%, , Hide
}
Promote() {
@@ -176,6 +192,10 @@ WorkspaceName(monitor, workspace, value) {
Run, komorebic.exe workspace-name %monitor% %workspace% %value%, , Hide
}
ToggleWindowContainerBehaviour() {
Run, komorebic.exe toggle-window-container-behaviour, , Hide
}
TogglePause() {
Run, komorebic.exe toggle-pause, , Hide
}
@@ -216,6 +236,10 @@ WatchConfiguration(boolean_state) {
Run, komorebic.exe watch-configuration %boolean_state%, , Hide
}
WindowHidingBehaviour(hiding_behaviour) {
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
}
FloatRule(identifier, id) {
Run, komorebic.exe float-rule %identifier% %id%, , Hide
}
@@ -244,6 +268,14 @@ ToggleFocusFollowsMouse(implementation) {
Run, komorebic.exe toggle-focus-follows-mouse --implementation %implementation%, , Hide
}
MouseFollowsFocus(boolean_state) {
Run, komorebic.exe mouse-follows-focus %boolean_state%, , Hide
}
ToggleMouseFollowsFocus() {
Run, komorebic.exe toggle-mouse-follows-focus, , Hide
}
AhkLibrary() {
Run, komorebic.exe ahk-library, , Hide
}
}

View File

@@ -1,6 +1,6 @@
[package]
name = "komorebic"
version = "0.1.6"
version = "0.1.7"
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"]
@@ -11,17 +11,23 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bindings = { package = "bindings", path = "../bindings" }
derive-ahk = { path = "../derive-ahk" }
komorebi-core = { path = "../komorebi-core" }
clap = "3.0.0-beta.4"
clap = { version = "3", features = ["derive", "wrap_help"] }
color-eyre = "0.5"
dirs = "4"
fs-tail = "0.1"
heck = "0.3"
heck = "0.4"
paste = "1"
powershell_script = "0.2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uds_windows = "1"
[dependencies.windows]
version = "0.30"
features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging"
]

View File

@@ -12,26 +12,27 @@ use std::process::Command;
use clap::AppSettings;
use clap::ArgEnum;
use clap::Clap;
use clap::Parser;
use color_eyre::eyre::anyhow;
use color_eyre::Result;
use fs_tail::TailedFile;
use heck::KebabCase;
use heck::ToKebabCase;
use paste::paste;
use uds_windows::UnixListener;
use uds_windows::UnixStream;
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use bindings::Windows::Win32::Foundation::HWND;
use bindings::Windows::Win32::UI::WindowsAndMessaging::ShowWindow;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use derive_ahk::AhkFunction;
use derive_ahk::AhkLibrary;
use komorebi_core::ApplicationIdentifier;
use komorebi_core::Axis;
use komorebi_core::CycleDirection;
use komorebi_core::DefaultLayout;
use komorebi_core::Flip;
use komorebi_core::FocusFollowsMouseImplementation;
use komorebi_core::HidingBehaviour;
use komorebi_core::OperationDirection;
use komorebi_core::Rect;
use komorebi_core::Sizing;
@@ -46,7 +47,7 @@ trait AhkFunction {
fn generate_ahk_function() -> String;
}
#[derive(ArgEnum)]
#[derive(Copy, Clone, ArgEnum)]
enum BooleanState {
Enable,
Disable,
@@ -66,7 +67,7 @@ macro_rules! gen_enum_subcommand_args {
( $( $name:ident: $element:ty ),+ $(,)? ) => {
$(
paste! {
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct $name {
#[clap(arg_enum)]
[<$element:snake>]: $element
@@ -85,17 +86,19 @@ gen_enum_subcommand_args! {
CycleWorkspace: CycleDirection,
Stack: OperationDirection,
CycleStack: CycleDirection,
FlipLayout: Flip,
FlipLayout: Axis,
ChangeLayout: DefaultLayout,
WatchConfiguration: BooleanState,
MouseFollowsFocus: BooleanState,
Query: StateQuery,
WindowHidingBehaviour: HidingBehaviour,
}
macro_rules! gen_target_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
$(
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct $name {
/// Target index (zero-indexed)
target: usize,
@@ -111,6 +114,7 @@ gen_target_subcommand_args! {
SendToWorkspace,
FocusMonitor,
FocusWorkspace,
MoveWorkspaceToMonitor,
}
// Thanks to @danielhenrymantilla for showing me how to use cfg_attr with an optional argument like
@@ -121,7 +125,7 @@ macro_rules! gen_workspace_subcommand_args {
( $( $name:ident: $(#[enum] $(@$arg_enum:tt)?)? $value:ty ),+ $(,)? ) => (
paste! {
$(
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct [<Workspace $name>] {
/// Monitor index (zero-indexed)
monitor: usize,
@@ -147,7 +151,7 @@ gen_workspace_subcommand_args! {
Tiling: #[enum] BooleanState,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
pub struct WorkspaceCustomLayout {
/// Monitor index (zero-indexed)
monitor: usize,
@@ -159,7 +163,7 @@ pub struct WorkspaceCustomLayout {
path: String,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct Resize {
#[clap(arg_enum)]
edge: OperationDirection,
@@ -167,7 +171,21 @@ struct Resize {
sizing: Sizing,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct ResizeAxis {
#[clap(arg_enum)]
axis: Axis,
#[clap(arg_enum)]
sizing: Sizing,
}
#[derive(Parser, AhkFunction)]
struct ResizeDelta {
/// The delta of pixels by which to increase or decrease window dimensions when resizing
pixels: i32,
}
#[derive(Parser, AhkFunction)]
struct InvisibleBorders {
/// Size of the left invisible border
left: i32,
@@ -179,7 +197,7 @@ struct InvisibleBorders {
bottom: i32,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct WorkAreaOffset {
/// Size of the left work area offset (set right to left * 2 to maintain right padding)
left: i32,
@@ -191,7 +209,7 @@ struct WorkAreaOffset {
bottom: i32,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct EnsureWorkspaces {
/// Monitor index (zero-indexed)
monitor: usize,
@@ -199,11 +217,19 @@ struct EnsureWorkspaces {
workspace_count: usize,
}
#[derive(Parser, AhkFunction)]
struct FocusMonitorWorkspace {
/// Target monitor index (zero-indexed)
target_monitor: usize,
/// Workspace index on the target monitor (zero-indexed)
target_workspace: usize,
}
macro_rules! gen_padding_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
$(
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct $name {
/// Monitor index (zero-indexed)
monitor: usize,
@@ -225,7 +251,7 @@ macro_rules! gen_padding_adjustment_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
$(
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct $name {
#[clap(arg_enum)]
sizing: Sizing,
@@ -245,7 +271,7 @@ macro_rules! gen_application_target_subcommand_args {
// SubCommand Pattern
( $( $name:ident ),+ $(,)? ) => {
$(
#[derive(clap::Clap, derive_ahk::AhkFunction)]
#[derive(clap::Parser, derive_ahk::AhkFunction)]
pub struct $name {
#[clap(arg_enum)]
identifier: ApplicationIdentifier,
@@ -261,10 +287,9 @@ gen_application_target_subcommand_args! {
ManageRule,
IdentifyTrayApplication,
IdentifyBorderOverflow,
RemoveTitleBar,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct WorkspaceRule {
#[clap(arg_enum)]
identifier: ApplicationIdentifier,
@@ -276,13 +301,13 @@ struct WorkspaceRule {
workspace: usize,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct ToggleFocusFollowsMouse {
#[clap(arg_enum, short, long, default_value = "windows")]
implementation: FocusFollowsMouseImplementation,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct FocusFollowsMouse {
#[clap(arg_enum, short, long, default_value = "windows")]
implementation: FocusFollowsMouseImplementation,
@@ -290,51 +315,51 @@ struct FocusFollowsMouse {
boolean_state: BooleanState,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct Start {
/// Allow the use of komorebi's custom focus-follows-mouse implementation
#[clap(long)]
ffm: bool,
}
#[derive(Clap, AhkFunction)]
struct Save {
#[derive(Parser, AhkFunction)]
struct SaveResize {
/// File to which the resize layout dimensions should be saved
path: String,
}
#[derive(Clap, AhkFunction)]
struct Load {
#[derive(Parser, AhkFunction)]
struct LoadResize {
/// File from which the resize layout dimensions should be loaded
path: String,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct LoadCustomLayout {
/// JSON or YAML file from which the custom layout definition should be loaded
path: String,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct Subscribe {
/// Name of the pipe to send event notifications to (without "\\.\pipe\" prepended)
named_pipe: String,
}
#[derive(Clap, AhkFunction)]
#[derive(Parser, AhkFunction)]
struct Unsubscribe {
/// Name of the pipe to stop sending event notifications to (without "\\.\pipe\" prepended)
named_pipe: String,
}
#[derive(Clap)]
#[derive(Parser)]
#[clap(author, about, version, setting = AppSettings::DeriveDisplayOrder)]
struct Opts {
#[clap(subcommand)]
subcmd: SubCommand,
}
#[derive(Clap, AhkLibrary)]
#[derive(Parser, AhkLibrary)]
enum SubCommand {
/// Start komorebi.exe as a background process
Start(Start),
@@ -354,15 +379,19 @@ enum SubCommand {
/// Tail komorebi.exe's process logs (cancel with Ctrl-C)
Log,
/// Quicksave the current resize layout dimensions
QuickSave,
#[clap(alias = "quick-save")]
QuickSaveResize,
/// Load the last quicksaved resize layout dimensions
QuickLoad,
#[clap(alias = "quick-load")]
QuickLoadResize,
/// Save the current resize layout dimensions to a file
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
Save(Save),
#[clap(alias = "save")]
SaveResize(SaveResize),
/// Load the resize layout dimensions from a file
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
Load(Load),
#[clap(alias = "load")]
LoadResize(LoadResize),
/// Change focus to the window in the specified direction
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
Focus(Focus),
@@ -380,7 +409,11 @@ enum SubCommand {
Stack(Stack),
/// Resize the focused window in the specified direction
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
Resize(Resize),
#[clap(alias = "resize")]
ResizeEdge(Resize),
/// Resize the focused window or primary column along the specified axis
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
ResizeAxis(ResizeAxis),
/// Unstack the focused window
Unstack,
/// Cycle the focused stack in the specified cycle direction
@@ -404,14 +437,23 @@ enum SubCommand {
/// Focus the specified workspace on the focused monitor
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
FocusWorkspace(FocusWorkspace),
/// Focus the specified workspace on the target monitor
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
FocusMonitorWorkspace(FocusMonitorWorkspace),
/// Focus the monitor in the given cycle direction
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
CycleMonitor(CycleMonitor),
/// Focus the workspace in the given cycle direction
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
CycleWorkspace(CycleWorkspace),
/// Move the focused workspace to the specified monitor
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
MoveWorkspaceToMonitor(MoveWorkspaceToMonitor),
/// Create and append a new workspace on the focused monitor
NewWorkspace,
/// Set the resize delta (used by resize-edge and resize-axis)
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
ResizeDelta(ResizeDelta),
/// Set the invisible border dimensions around each window
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
InvisibleBorders(InvisibleBorders),
@@ -458,7 +500,9 @@ enum SubCommand {
/// Set the workspace name for the specified workspace
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
WorkspaceName(WorkspaceName),
/// Toggle the window manager on and off across all monitors
/// Toggle the behaviour for new windows (stacking or dynamic tiling)
ToggleWindowContainerBehaviour,
/// Toggle window tiling on the focused workspace
TogglePause,
/// Toggle window tiling on the focused workspace
ToggleTiling,
@@ -479,6 +523,9 @@ enum SubCommand {
/// Enable or disable watching of ~/komorebi.ahk (if it exists)
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
WatchConfiguration(WatchConfiguration),
/// Set the window behaviour when switching workspaces / cycling stacks
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
WindowHidingBehaviour(WindowHidingBehaviour),
/// Add a rule to always float the specified application
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
FloatRule(FloatRule),
@@ -494,17 +541,17 @@ enum SubCommand {
/// Identify an application that has overflowing borders
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
IdentifyBorderOverflow(IdentifyBorderOverflow),
/// Whitelist an application for title bar removal
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
RemoveTitleBar(RemoveTitleBar),
/// Toggle title bars for whitelisted applications
ToggleTitleBars,
/// Enable or disable focus follows mouse for the operating system
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
FocusFollowsMouse(FocusFollowsMouse),
/// Toggle focus follows mouse for the operating system
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
ToggleFocusFollowsMouse(ToggleFocusFollowsMouse),
/// Enable or disable mouse follows focus on all workspaces
#[clap(setting = AppSettings::ArgRequiredElseHelp)]
MouseFollowsFocus(MouseFollowsFocus),
/// Toggle mouse follows focus on all workspaces
ToggleMouseFollowsFocus,
/// Generate a library of AutoHotKey helper functions
AhkLibrary,
}
@@ -590,6 +637,9 @@ fn main() -> Result<()> {
SubCommand::SendToWorkspace(arg) => {
send_message(&*SocketMessage::SendContainerToWorkspaceNumber(arg.target).as_bytes()?)?;
}
SubCommand::MoveWorkspaceToMonitor(arg) => {
send_message(&*SocketMessage::MoveWorkspaceToMonitorNumber(arg.target).as_bytes()?)?;
}
SubCommand::InvisibleBorders(arg) => {
send_message(
&*SocketMessage::InvisibleBorders(Rect {
@@ -757,7 +807,7 @@ fn main() -> Result<()> {
)?;
}
SubCommand::FlipLayout(arg) => {
send_message(&*SocketMessage::FlipLayout(arg.flip).as_bytes()?)?;
send_message(&*SocketMessage::FlipLayout(arg.axis).as_bytes()?)?;
}
SubCommand::FocusMonitor(arg) => {
send_message(&*SocketMessage::FocusMonitorNumber(arg.target).as_bytes()?)?;
@@ -765,6 +815,15 @@ fn main() -> Result<()> {
SubCommand::FocusWorkspace(arg) => {
send_message(&*SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?;
}
SubCommand::FocusMonitorWorkspace(arg) => {
send_message(
&*SocketMessage::FocusMonitorWorkspaceNumber(
arg.target_monitor,
arg.target_workspace,
)
.as_bytes()?,
)?;
}
SubCommand::CycleMonitor(arg) => {
send_message(&*SocketMessage::CycleFocusMonitor(arg.cycle_direction).as_bytes()?)?;
}
@@ -867,28 +926,27 @@ fn main() -> Result<()> {
restore_window(HWND(hwnd));
}
}
SubCommand::Resize(resize) => {
send_message(&*SocketMessage::ResizeWindow(resize.edge, resize.sizing).as_bytes()?)?;
SubCommand::ResizeEdge(resize) => {
send_message(
&*SocketMessage::ResizeWindowEdge(resize.edge, resize.sizing).as_bytes()?,
)?;
}
SubCommand::ResizeAxis(arg) => {
send_message(&*SocketMessage::ResizeWindowAxis(arg.axis, arg.sizing).as_bytes()?)?;
}
SubCommand::FocusFollowsMouse(arg) => {
let enable = match arg.boolean_state {
BooleanState::Enable => true,
BooleanState::Disable => false,
};
send_message(
&*SocketMessage::FocusFollowsMouse(arg.implementation, enable).as_bytes()?,
&*SocketMessage::FocusFollowsMouse(arg.implementation, arg.boolean_state.into())
.as_bytes()?,
)?;
}
SubCommand::ReloadConfiguration => {
send_message(&*SocketMessage::ReloadConfiguration.as_bytes()?)?;
}
SubCommand::WatchConfiguration(arg) => {
let enable = match arg.boolean_state {
BooleanState::Enable => true,
BooleanState::Disable => false,
};
send_message(&*SocketMessage::WatchConfiguration(enable).as_bytes()?)?;
send_message(
&*SocketMessage::WatchConfiguration(arg.boolean_state.into()).as_bytes()?,
)?;
}
SubCommand::IdentifyTrayApplication(target) => {
send_message(
@@ -901,39 +959,22 @@ fn main() -> Result<()> {
&*SocketMessage::IdentifyBorderOverflow(target.identifier, target.id).as_bytes()?,
)?;
}
SubCommand::RemoveTitleBar(target) => {
match target.identifier {
ApplicationIdentifier::Exe => {}
_ => {
return Err(anyhow!(
"this command requires applications to be identified by their exe"
))
}
}
send_message(
&*SocketMessage::RemoveTitleBar(target.identifier, target.id).as_bytes()?,
)?;
}
SubCommand::ToggleTitleBars => {
send_message(&*SocketMessage::ToggleTitleBars.as_bytes()?)?;
}
SubCommand::Manage => {
send_message(&*SocketMessage::ManageFocusedWindow.as_bytes()?)?;
}
SubCommand::Unmanage => {
send_message(&*SocketMessage::UnmanageFocusedWindow.as_bytes()?)?;
}
SubCommand::QuickSave => {
SubCommand::QuickSaveResize => {
send_message(&*SocketMessage::QuickSave.as_bytes()?)?;
}
SubCommand::QuickLoad => {
SubCommand::QuickLoadResize => {
send_message(&*SocketMessage::QuickLoad.as_bytes()?)?;
}
SubCommand::Save(arg) => {
SubCommand::SaveResize(arg) => {
send_message(&*SocketMessage::Save(resolve_windows_path(&arg.path)?).as_bytes()?)?;
}
SubCommand::Load(arg) => {
SubCommand::LoadResize(arg) => {
send_message(&*SocketMessage::Load(resolve_windows_path(&arg.path)?).as_bytes()?)?;
}
SubCommand::Subscribe(arg) => {
@@ -942,6 +983,21 @@ fn main() -> Result<()> {
SubCommand::Unsubscribe(arg) => {
send_message(&*SocketMessage::RemoveSubscriber(arg.named_pipe).as_bytes()?)?;
}
SubCommand::ToggleMouseFollowsFocus => {
send_message(&*SocketMessage::ToggleMouseFollowsFocus.as_bytes()?)?;
}
SubCommand::MouseFollowsFocus(arg) => {
send_message(&*SocketMessage::MouseFollowsFocus(arg.boolean_state.into()).as_bytes()?)?;
}
SubCommand::ResizeDelta(arg) => {
send_message(&*SocketMessage::ResizeDelta(arg.pixels).as_bytes()?)?;
}
SubCommand::ToggleWindowContainerBehaviour => {
send_message(&*SocketMessage::ToggleWindowContainerBehaviour.as_bytes()?)?;
}
SubCommand::WindowHidingBehaviour(arg) => {
send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?;
}
}
Ok(())
@@ -950,7 +1006,7 @@ fn main() -> Result<()> {
fn resolve_windows_path(raw_path: &str) -> Result<PathBuf> {
let path = if raw_path.starts_with('~') {
raw_path.replacen(
"~",
'~',
&dirs::home_dir()
.ok_or_else(|| anyhow!("there is no home directory"))?
.display()