mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-18 14:17:01 +01:00
Compare commits
21 Commits
master-aft
...
feature/no
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0f6d1a465c | ||
|
|
40e77fddfe | ||
|
|
9c8a50fe80 | ||
|
|
41e9068fca | ||
|
|
3690f8ebd8 | ||
|
|
7b24474ef2 | ||
|
|
f675717844 | ||
|
|
38b0418c3b | ||
|
|
6781f34930 | ||
|
|
4919872e1a | ||
|
|
d730c3c72d | ||
|
|
4e98d7d36d | ||
|
|
2bceff4edc | ||
|
|
b32bce8713 | ||
|
|
47af40cf9e | ||
|
|
2b9fbc2074 | ||
|
|
1a8b6a7398 | ||
|
|
e5cf042ea9 | ||
|
|
c435f84afc | ||
|
|
de0db4d014 | ||
|
|
0afcf6d86a |
12
.github/workflows/windows.yaml
vendored
12
.github/workflows/windows.yaml
vendored
@@ -73,6 +73,11 @@ jobs:
|
||||
- name: Install the target
|
||||
run: |
|
||||
rustup target install ${{ matrix.target }}
|
||||
- name: Run Cargo checks
|
||||
run: |
|
||||
cargo fmt --check
|
||||
cargo check
|
||||
cargo clippy
|
||||
- name: Run a full build
|
||||
run: |
|
||||
cargo build --locked --release --target ${{ matrix.target }}
|
||||
@@ -93,6 +98,11 @@ jobs:
|
||||
target/${{ matrix.target }}/release/komorebic-no-console.pdb
|
||||
target/wix/komorebi-*.msi
|
||||
retention-days: 7
|
||||
- name: Check GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: build --skip=validate --clean
|
||||
|
||||
# Release
|
||||
- name: Generate changelog
|
||||
@@ -106,7 +116,7 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
version: latest
|
||||
args: release --skip-validate --clean --release-notes=CHANGELOG.md
|
||||
args: release --skip=validate --clean --release-notes=CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SCOOP_TOKEN: ${{ secrets.SCOOP_TOKEN }}
|
||||
|
||||
483
Cargo.lock
generated
483
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -4,20 +4,22 @@ resolver = "2"
|
||||
members = [
|
||||
"derive-ahk",
|
||||
"komorebi",
|
||||
"komorebi-client",
|
||||
"komorebi-core",
|
||||
"komorebic",
|
||||
"komorebic-no-console",
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
windows-interface = { version = "0.52" }
|
||||
windows-implement = { version = "0.52" }
|
||||
windows-interface = { version = "0.53" }
|
||||
windows-implement = { version = "0.53" }
|
||||
dunce = "1"
|
||||
dirs = "5"
|
||||
color-eyre = "0.6"
|
||||
serde_json = { package = "serde_json_lenient", version = "0.1" }
|
||||
|
||||
[workspace.dependencies.windows]
|
||||
version = "0.52"
|
||||
version = "0.54"
|
||||
features = [
|
||||
"implement",
|
||||
"Win32_System_Com",
|
||||
|
||||
200
README.md
200
README.md
@@ -101,20 +101,76 @@ widget enabled. The original video can be viewed
|
||||
|
||||
https://user-images.githubusercontent.com/13164844/163496414-a9cde3d1-b8a7-4a7a-96fb-a8985380bc70.mp4
|
||||
|
||||
# Development
|
||||
# Contribution Guidelines
|
||||
|
||||
If you would like to contribute code to this repository, there are a few requests that I have to ensure a foundation of
|
||||
code quality, consistency and commit hygiene:
|
||||
If you would like to contribute to `komorebi` please take the time to carefully read the guidelines below.
|
||||
|
||||
## Commit hygiene
|
||||
|
||||
- Flatten all `use` statements
|
||||
- Run `cargo +nightly clippy` and ensure that all lints and suggestions have been addressed before committing
|
||||
- Run `cargo +stable clippy` and ensure that all lints and suggestions have been addressed before committing
|
||||
- Run `cargo +nightly fmt --all` to ensure consistent formatting before committing
|
||||
- Use `git cz` with
|
||||
the [Commitizen CLI](https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility) to prepare
|
||||
commit messages
|
||||
- Provide at least one short sentence or paragraph in your commit message body to describe your thought process for the
|
||||
- Provide **at least** one short sentence or paragraph in your commit message body to describe your thought process for the
|
||||
changes being committed
|
||||
|
||||
## PRs should contain only a single feature or bug fix
|
||||
|
||||
It is very difficult to review pull requests which touch multiple unrelated features and parts of the codebase.
|
||||
|
||||
Please do not submit pull requests like this; you will be asked to separate them into smaller PRs that deal only with
|
||||
one feature or bug fix at a time.
|
||||
|
||||
If you are working on multiple features and bug fixes, I suggest that you cut a branch called `local-trunk`
|
||||
from `master` which you keep up to date, and rebase the various independent branches you are working on onto that branch
|
||||
if you want to test them together or create a build with everything integrated.
|
||||
|
||||
## Refactors to the codebase must have prior approval
|
||||
|
||||
`komorebi` is a mature codebase with an internal consistency and structure that has developed organically over close to
|
||||
half a decade.
|
||||
|
||||
There are [countless hours of live coding videos](https://youtube.com/@LGUG2Z) demonstrating work on this project and
|
||||
showing new contributors how to do everything from basic tasks like implementing new `komorebic` commands to
|
||||
distinguishing monitors by manufacturer hardware identifiers and video card ports.
|
||||
|
||||
Refactors to the structure of the codebase are not taken lightly and require prior discussion and approval.
|
||||
|
||||
Please do not start refactoring the codebase with the expectation of having your changes integrated until you receive an
|
||||
explicit approval or a request to do so.
|
||||
|
||||
Similarly, when implementing features and bug fixes, please stick to the structure of the codebase as much as possible
|
||||
and do not take this as an opportunity to do some "refactoring along the way".
|
||||
|
||||
It is extremely difficult to review PRs for features and bug fixes if they are lost in sweeping changes to the structure
|
||||
of the codebase.
|
||||
|
||||
## Breaking changes to user-facing interfaces are unacceptable
|
||||
|
||||
This includes but is not limited to:
|
||||
|
||||
- All `komorebic` commands
|
||||
- The `komorebi.json` schema
|
||||
- The [`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
|
||||
schema
|
||||
|
||||
No user should ever find that their configuration file has stopped working after upgrading to a new version
|
||||
of `komorebi`.
|
||||
|
||||
More often than not there are ways to reformulate changes that may initially seem like they require breaking user-facing
|
||||
interfaces into additive changes.
|
||||
|
||||
For some inspiration please take a look
|
||||
at [this commit](https://github.com/LGUG2Z/komorebi/commit/e7d928a065eb63bb4ea1fb864c69c1cae8cc763b) which added the
|
||||
ability for users to specify colours in `komorebi.json` in Hex format alongside RGB.
|
||||
|
||||
There is also a process in place for graceful, non-breaking, deprecation of configuration options that are no longer
|
||||
required.
|
||||
|
||||
# Development
|
||||
|
||||
If you use IntelliJ, you should enable the following settings to ensure that code generated by macros is recognised by
|
||||
the IDE for completions and navigation:
|
||||
|
||||
@@ -153,20 +209,21 @@ found, information about it will appear in the log which can be shared when open
|
||||
# Window Manager State and Integrations
|
||||
|
||||
The current state of the window manager can be queried using the `komorebic state` command, which returns a JSON
|
||||
representation of the `State` struct, which includes the current state of `WindowManager`.
|
||||
representation of the `State` struct.
|
||||
|
||||
This may also be polled to build further integrations and widgets on top of (if you ever wanted to build something
|
||||
like [Stackline](https://github.com/AdamWagner/stackline) for Windows, you could do it by polling this command).
|
||||
This may also be polled to build further integrations and widgets on top of.
|
||||
|
||||
# Window Manager Event Subscriptions
|
||||
|
||||
It is also possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
|
||||
## Named Pipes
|
||||
|
||||
It is possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
|
||||
by `komorebi` using [Named Pipes](https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes).
|
||||
|
||||
First, your application must create a named pipe. Once the named pipe has been created, run the following command:
|
||||
|
||||
```powershell
|
||||
komorebic.exe subscribe <your pipe name>
|
||||
komorebic.exe subscribe-pipe <your pipe name>
|
||||
```
|
||||
|
||||
Note that you do not have to include the full path of the named pipe, just the name.
|
||||
@@ -190,12 +247,125 @@ You may then filter on the `type` key to listen to the events that you are inter
|
||||
notification types, refer to the enum variants of `WindowManagerEvent` in `komorebi` and `SocketMessage`
|
||||
in `komorebi-core`.
|
||||
|
||||
An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Python
|
||||
by [@denBot](https://github.com/denBot) can be
|
||||
found [here](https://gist.github.com/denBot/4136279812f87819f86d99eba77c1ee0).
|
||||
Below is an example of how you can subscribe to and filter on events using a named pipe in `nodejs`.
|
||||
|
||||
An example of how to create a named pipe and a subscription to `komorebi`'s handled events in Rust can also be found
|
||||
in the [`komokana`](https://github.com/LGUG2Z/komokana) repository.
|
||||
```javascript
|
||||
const { exec } = require("child_process");
|
||||
const net = require("net");
|
||||
|
||||
const pipeName = "\\\\.\\pipe\\komorebi-js";
|
||||
const server = net.createServer((stream) => {
|
||||
console.log("Client connected");
|
||||
|
||||
// Every time there is a workspace-related event, let's log the names of all
|
||||
// workspaces on the currently focused monitor, and then log the name of the
|
||||
// currently focused workspace on that monitor
|
||||
|
||||
stream.on("data", (data) => {
|
||||
let json = JSON.parse(data.toString());
|
||||
let event = json.event;
|
||||
|
||||
if (event.type.includes("Workspace")) {
|
||||
let monitors = json.state.monitors;
|
||||
let current_monitor = monitors.elements[monitors.focused];
|
||||
let workspaces = monitors.elements[monitors.focused].workspaces;
|
||||
let current_workspace = workspaces.elements[workspaces.focused];
|
||||
|
||||
console.log(
|
||||
workspaces.elements
|
||||
.map((workspace) => workspace.name)
|
||||
.filter((name) => name !== null)
|
||||
);
|
||||
console.log(current_workspace.name);
|
||||
}
|
||||
});
|
||||
|
||||
stream.on("end", () => {
|
||||
console.log("Client disconnected");
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(pipeName, () => {
|
||||
console.log("Named pipe server listening");
|
||||
});
|
||||
|
||||
const command = "komorebic subscribe-pipe komorebi-js";
|
||||
|
||||
exec(command, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Error executing command: ${error}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Unix Domain Sockets
|
||||
|
||||
It is possible to subscribe to notifications of every `WindowManagerEvent` and `SocketMessage` handled
|
||||
by `komorebi` using [Unix Domain Sockets](https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/).
|
||||
|
||||
UDS are also the only mode of communication between `komorebi` and `komorebic`.
|
||||
|
||||
First, your application must create a socket in `$ENV:LocalAppData\komorebi`. Once the socket has been created, run the
|
||||
following command:
|
||||
|
||||
```powershell
|
||||
komorebic.exe subscribe-socket <your socket name>
|
||||
```
|
||||
|
||||
If the socket exists, komorebi will start pushing JSON data of successfully handled events and messages as in the
|
||||
example above in the Named Pipes section.
|
||||
|
||||
## Rust Client
|
||||
|
||||
As of `v0.1.22` it is possible to use the `komorebi-client` crate to subscribe to notifications of
|
||||
every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust codebase.
|
||||
|
||||
Below is a simple example of how to use `komorebi-client` in a basic Rust application.
|
||||
|
||||
```rust
|
||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.22"}
|
||||
|
||||
use anyhow::Result;
|
||||
use komorebi_client::Notification;
|
||||
use komorebi_client::NotificationEvent;
|
||||
use komorebi_client::UnixListener;
|
||||
use komorebi_client::WindowManagerEvent;
|
||||
use std::io::BufRead;
|
||||
use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
|
||||
pub fn main() -> anyhow::Result<()> {
|
||||
let socket = komorebi_client::subscribe(NAME)?;
|
||||
|
||||
for incoming in socket.incoming() {
|
||||
match incoming {
|
||||
Ok(data) => {
|
||||
let reader = BufReader::new(data.try_clone()?);
|
||||
|
||||
for line in reader.lines().flatten() {
|
||||
let notification: Notification = match serde_json::from_str(&line) {
|
||||
Ok(notification) => notification,
|
||||
Err(error) => {
|
||||
log::debug!("discarding malformed komorebi notification: {error}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// match and filter on desired notifications
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
log::debug!("{error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
A read-world example can be found
|
||||
in [komokana](https://github.com/LGUG2Z/komokana/blob/feature/komorebi-uds/src/main.rs).
|
||||
|
||||
## Subscription Event Notification Schema
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# alt-focus-hack
|
||||
|
||||
```
|
||||
Enable or disable a hack simulating ALT key presses to ensure focus changes succeed
|
||||
DEPRECATED since v0.1.22
|
||||
|
||||
Usage: komorebic.exe alt-focus-hack <BOOLEAN_STATE>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe change-layout <DEFAULT_LAYOUT>
|
||||
|
||||
Arguments:
|
||||
<DEFAULT_LAYOUT>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# check
|
||||
|
||||
```
|
||||
Output various important komorebi-related environment values
|
||||
Check komorebi configuration and related files for common errors
|
||||
|
||||
Usage: komorebic.exe check
|
||||
|
||||
|
||||
12
docs/cli/configuration.md
Normal file
12
docs/cli/configuration.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# configuration
|
||||
|
||||
```
|
||||
Show the path to komorebi.json
|
||||
|
||||
Usage: komorebic.exe configuration
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,10 +0,0 @@
|
||||
# docgen
|
||||
|
||||
```
|
||||
Usage: komorebic.exe docgen
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -13,7 +13,7 @@ Arguments:
|
||||
The number of window containers on-screen required to trigger this layout rule
|
||||
|
||||
<LAYOUT>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -10,7 +10,7 @@ Arguments:
|
||||
Target workspace name
|
||||
|
||||
<VALUE>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# subscribe
|
||||
# subscribe-pipe
|
||||
|
||||
```
|
||||
Subscribe to komorebi events
|
||||
Subscribe to komorebi events using a Named Pipe
|
||||
|
||||
Usage: komorebic.exe subscribe <NAMED_PIPE>
|
||||
Usage: komorebic.exe subscribe-pipe <NAMED_PIPE>
|
||||
|
||||
Arguments:
|
||||
<NAMED_PIPE>
|
||||
16
docs/cli/subscribe-socket.md
Normal file
16
docs/cli/subscribe-socket.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# subscribe-socket
|
||||
|
||||
```
|
||||
Subscribe to komorebi events using a Unix Domain Socket
|
||||
|
||||
Usage: komorebic.exe subscribe-socket <SOCKET>
|
||||
|
||||
Arguments:
|
||||
<SOCKET>
|
||||
Name of the socket to send event notifications to
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,9 +1,9 @@
|
||||
# unsubscribe
|
||||
# unsubscribe-pipe
|
||||
|
||||
```
|
||||
Unsubscribe from komorebi events
|
||||
|
||||
Usage: komorebic.exe unsubscribe <NAMED_PIPE>
|
||||
Usage: komorebic.exe unsubscribe-pipe <NAMED_PIPE>
|
||||
|
||||
Arguments:
|
||||
<NAMED_PIPE>
|
||||
16
docs/cli/unsubscribe-socket.md
Normal file
16
docs/cli/unsubscribe-socket.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# unsubscribe-socket
|
||||
|
||||
```
|
||||
Unsubscribe from komorebi events
|
||||
|
||||
Usage: komorebic.exe unsubscribe-socket <SOCKET>
|
||||
|
||||
Arguments:
|
||||
<SOCKET>
|
||||
Name of the socket to stop sending event notifications to
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
12
docs/cli/whkdrc.md
Normal file
12
docs/cli/whkdrc.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# whkdrc
|
||||
|
||||
```
|
||||
Show the path to whkdrc
|
||||
|
||||
Usage: komorebic.exe whkdrc
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -16,7 +16,7 @@ Arguments:
|
||||
The number of window containers on-screen required to trigger this layout rule
|
||||
|
||||
<LAYOUT>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -13,7 +13,7 @@ Arguments:
|
||||
Workspace index on the specified monitor (zero-indexed)
|
||||
|
||||
<VALUE>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
|
||||
If you would like to remove all gaps by default, both between windows
|
||||
themselves, and between the monitor edges and the windows, you can set the
|
||||
following two configuration options to `0` in the `komorebi.json` configuration
|
||||
file.
|
||||
following configuration options to `0` and `-1` in the `komorebi.json`
|
||||
configuration file.
|
||||
|
||||
```json
|
||||
{
|
||||
"default_workspace_padding": 0,
|
||||
"default_container_padding": 0
|
||||
"default_container_padding": 0,
|
||||
"border_width": 0,
|
||||
"border_offset": -1
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ the example files have been downloaded. For most new users this will be in the
|
||||
komorebic quickstart
|
||||
```
|
||||
|
||||
With the example configurations downloaded, you can now start `komorebi` and `whkd.
|
||||
|
||||
```powershell
|
||||
komorebic start --whkd
|
||||
```
|
||||
|
||||
## komorebi.json
|
||||
|
||||
The example window manager configuration sets some sane defaults and provides
|
||||
@@ -139,6 +145,19 @@ If you have an ultrawide monitor, I recommend using this layout.
|
||||
+-----+-----------+-----+
|
||||
```
|
||||
|
||||
### Grid
|
||||
If you like the `grid` layout in [LeftWM](https://github.com/leftwm/leftwm-layouts) this is almost exactly the same!
|
||||
|
||||
```
|
||||
+-----+-----+ +---+---+---+ +---+---+---+ +---+---+---+
|
||||
| | | | | | | | | | | | | | |
|
||||
| | | | | | | | | | | | | +---+
|
||||
+-----+-----+ | +---+---+ +---+---+---+ +---+---| |
|
||||
| | | | | | | | | | | | | +---+
|
||||
| | | | | | | | | | | | | | |
|
||||
+-----+-----+ +---+---+---+ +---+---+---+ +---+---+---+
|
||||
4 windows 5 windows 6 windows 7 windows
|
||||
```
|
||||
|
||||
## whkdrc
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.20/schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.22/schema.json",
|
||||
"app_specific_configuration_path": "$Env:USERPROFILE/applications.yaml",
|
||||
"window_hiding_behaviour": "Cloak",
|
||||
"cross_monitor_move_behaviour": "Insert",
|
||||
"default_workspace_padding": 20,
|
||||
"default_container_padding": 20,
|
||||
"border_padding": 8,
|
||||
"border_offset": -1,
|
||||
"active_window_border": false,
|
||||
"active_window_border_colours": {
|
||||
"single": { "r": 66, "g": 165, "b": 245 },
|
||||
"stack": { "r": 256, "g": 165, "b": 66 },
|
||||
"monocle": { "r": 255, "g": 51, "b": 153 }
|
||||
"single": "#42a5f5",
|
||||
"stack": "#00a542",
|
||||
"monocle": "#ff3399"
|
||||
},
|
||||
"monitors": [
|
||||
{
|
||||
|
||||
56
docs/release/v0-1-22.md
Normal file
56
docs/release/v0-1-22.md
Normal file
@@ -0,0 +1,56 @@
|
||||
In addition to the [changelog](https://github.com/LGUG2Z/komorebi/releases/tag/v0.1.22) of new features and fixes,
|
||||
please note the following changes from `v0.1.21` to adjust your configuration files accordingly.
|
||||
|
||||
## tl;dr
|
||||
|
||||
The way windows are sized and drawn has been improved to remove the need to manually specify and remove invisible
|
||||
borders for applications that overflow them. If you use the active window border, the first time you launch `v0.1.22`
|
||||
you may end up with a _huge_ border due to these changes.
|
||||
|
||||
`active_window_border_width` and `active_window_border_offset` have been renamed to `border_width` and `border_offset`
|
||||
as they now also apply outside the context of the active window border.
|
||||
|
||||
```json
|
||||
{
|
||||
"active_window_border": true,
|
||||
"border_width": 8,
|
||||
"border_offset": -1
|
||||
}
|
||||
```
|
||||
|
||||
Users of the active window border should start from these settings and read the notes below before making further
|
||||
adjustments.
|
||||
|
||||
## Changes to `active_window_border`, and window sizing:
|
||||
|
||||
- The border no longer creates a second drop-shadow around the active window
|
||||
- Windows are now sized to fill the layout region entirely, ignoring window decorations such as drop shadows
|
||||
- Border offset now starts exactly at the paint edge of the window on all sides
|
||||
- Windows are sized such that the border offset and border width are taken into account
|
||||
|
||||
## Recommended patterns
|
||||
|
||||
### Gapless
|
||||
|
||||
- Disable "transparency effects" Personalization > Colors
|
||||
- Set the following settings in `komorebi.json`:
|
||||
```json
|
||||
{
|
||||
"default_workspace_padding": 0,
|
||||
"default_container_padding": 0,
|
||||
"border_offset": -1,
|
||||
"border_width": 0
|
||||
}
|
||||
```
|
||||
|
||||
### 1px border
|
||||
|
||||
A 1px border is drawn around the window edge. Users may see a gap for a single pixel, if the system theme has a
|
||||
transparent edge - this is the windows themed edge, and is not present for all applications.
|
||||
|
||||
```json
|
||||
{
|
||||
"border_offset": 0,
|
||||
"border_width": 1
|
||||
}
|
||||
```
|
||||
12
komorebi-client/Cargo.toml
Normal file
12
komorebi-client/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "komorebi-client"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
komorebi = { path = "../komorebi" }
|
||||
komorebi-core = { path = "../komorebi-core" }
|
||||
uds_windows = "1"
|
||||
serde_json = { workspace = true }
|
||||
79
komorebi-client/src/lib.rs
Normal file
79
komorebi-client/src/lib.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
|
||||
pub use komorebi::container::Container;
|
||||
pub use komorebi::monitor::Monitor;
|
||||
pub use komorebi::ring::Ring;
|
||||
pub use komorebi::window::Window;
|
||||
pub use komorebi::window_manager_event::WindowManagerEvent;
|
||||
pub use komorebi::workspace::Workspace;
|
||||
pub use komorebi::Notification;
|
||||
pub use komorebi::NotificationEvent;
|
||||
pub use komorebi::State;
|
||||
pub use komorebi_core::Arrangement;
|
||||
pub use komorebi_core::Axis;
|
||||
pub use komorebi_core::CustomLayout;
|
||||
pub use komorebi_core::CycleDirection;
|
||||
pub use komorebi_core::DefaultLayout;
|
||||
pub use komorebi_core::Direction;
|
||||
pub use komorebi_core::Layout;
|
||||
pub use komorebi_core::OperationDirection;
|
||||
pub use komorebi_core::Rect;
|
||||
pub use komorebi_core::SocketMessage;
|
||||
|
||||
use komorebi::DATA_DIR;
|
||||
|
||||
use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::net::Shutdown;
|
||||
pub use uds_windows::UnixListener;
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
const KOMOREBI: &str = "komorebi.sock";
|
||||
|
||||
pub fn send_message(message: &SocketMessage) -> std::io::Result<()> {
|
||||
let socket = DATA_DIR.join(KOMOREBI);
|
||||
let mut connected = false;
|
||||
while !connected {
|
||||
if let Ok(mut stream) = UnixStream::connect(&socket) {
|
||||
connected = true;
|
||||
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_query(message: &SocketMessage) -> std::io::Result<String> {
|
||||
let socket = DATA_DIR.join(KOMOREBI);
|
||||
|
||||
let mut stream = UnixStream::connect(socket)?;
|
||||
stream.write_all(serde_json::to_string(message)?.as_bytes())?;
|
||||
stream.shutdown(Shutdown::Write)?;
|
||||
|
||||
let mut reader = BufReader::new(stream);
|
||||
let mut response = String::new();
|
||||
reader.read_to_string(&mut response)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn subscribe(name: &str) -> std::io::Result<UnixListener> {
|
||||
let socket = DATA_DIR.join(name);
|
||||
|
||||
match std::fs::remove_file(&socket) {
|
||||
Ok(()) => {}
|
||||
Err(error) => match error.kind() {
|
||||
std::io::ErrorKind::NotFound => {}
|
||||
_ => {
|
||||
return Err(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let listener = UnixListener::bind(&socket)?;
|
||||
|
||||
send_message(&SocketMessage::AddSubscriberSocket(name.to_string()))?;
|
||||
|
||||
Ok(listener)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-core"
|
||||
version = "0.1.22-dev.0"
|
||||
version = "0.1.22"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -8,7 +8,7 @@ edition = "2021"
|
||||
[dependencies]
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = "0.9"
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
|
||||
@@ -102,7 +102,7 @@ impl ApplicationConfiguration {
|
||||
pub fn populate_default_matching_strategies(&mut self) {
|
||||
if self.identifier.matching_strategy.is_none() {
|
||||
match self.identifier.kind {
|
||||
ApplicationIdentifier::Exe => {
|
||||
ApplicationIdentifier::Exe | ApplicationIdentifier::Path => {
|
||||
self.identifier.matching_strategy = Option::from(MatchingStrategy::Equals);
|
||||
}
|
||||
ApplicationIdentifier::Class | ApplicationIdentifier::Title => {}
|
||||
|
||||
@@ -223,6 +223,8 @@ pub enum ApplicationIdentifier {
|
||||
Class,
|
||||
#[serde(alias = "title")]
|
||||
Title,
|
||||
#[serde(alias = "path")]
|
||||
Path,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.20/schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.22/schema.json",
|
||||
"app_specific_configuration_path": "$Env:USERPROFILE/applications.yaml",
|
||||
"window_hiding_behaviour": "Cloak",
|
||||
"cross_monitor_move_behaviour": "Insert",
|
||||
"default_workspace_padding": 20,
|
||||
"default_container_padding": 20,
|
||||
"border_padding": 8,
|
||||
"border_offset": -1,
|
||||
"active_window_border": false,
|
||||
"active_window_border_colours": {
|
||||
"single": { "r": 66, "g": 165, "b": 245 },
|
||||
"stack": { "r": 256, "g": 165, "b": 66 },
|
||||
"monocle": { "r": 255, "g": 51, "b": 153 }
|
||||
"single": "#42a5f5",
|
||||
"stack": "#00a542",
|
||||
"monocle": "#ff3399"
|
||||
},
|
||||
"monitors": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi"
|
||||
version = "0.1.22-dev.0"
|
||||
version = "0.1.22"
|
||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||
description = "A tiling window manager for Windows"
|
||||
categories = ["tiling-window-manager", "windows"]
|
||||
@@ -22,7 +22,7 @@ ctrlc = "3"
|
||||
dirs = { workspace = true }
|
||||
getset = "0.1"
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
hotwatch = "0.4"
|
||||
hotwatch = "0.5"
|
||||
lazy_static = "1"
|
||||
miow = "0.5"
|
||||
nanoid = "0.4"
|
||||
@@ -33,7 +33,7 @@ paste = "1"
|
||||
regex = "1"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_json = { workspace = true }
|
||||
strum = { version = "0.26", features = ["derive"] }
|
||||
sysinfo = "0.30"
|
||||
tracing = "0.1"
|
||||
|
||||
@@ -10,7 +10,6 @@ use interfaces::IServiceProvider;
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
use windows::core::ComInterface;
|
||||
use windows::core::Interface;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::System::Com::CoCreateInstance;
|
||||
|
||||
@@ -170,7 +170,7 @@ lazy_static! {
|
||||
}
|
||||
})
|
||||
};
|
||||
static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
|
||||
pub static ref DATA_DIR: PathBuf = dirs::data_local_dir().expect("there is no local data directory").join("komorebi");
|
||||
pub static ref AHK_EXE: String = {
|
||||
let mut ahk: String = String::from("autohotkey.exe");
|
||||
|
||||
@@ -192,7 +192,6 @@ lazy_static! {
|
||||
static ref BORDER_RECT: Arc<Mutex<Rect>> =
|
||||
Arc::new(Mutex::new(Rect::default()));
|
||||
|
||||
static ref BORDER_OFFSET: AtomicI32 = Default::default();
|
||||
|
||||
// Use app-specific titlebar removal options where possible
|
||||
// eg. Windows Terminal, IntelliJ IDEA, Firefox
|
||||
@@ -212,7 +211,9 @@ pub static BORDER_COLOUR_SINGLE: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_STACK: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_MONOCLE: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_COLOUR_CURRENT: AtomicU32 = AtomicU32::new(0);
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(20);
|
||||
pub static BORDER_WIDTH: AtomicI32 = AtomicI32::new(8);
|
||||
pub static BORDER_OFFSET: AtomicI32 = AtomicI32::new(-1);
|
||||
|
||||
// 0 0 0 aka pure black, I doubt anyone will want this as a border colour
|
||||
pub const TRANSPARENCY_COLOUR: u32 = 0;
|
||||
pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -315,6 +315,11 @@ impl WindowManager {
|
||||
{
|
||||
for window in container.windows() {
|
||||
match identifier {
|
||||
ApplicationIdentifier::Path => {
|
||||
if window.path()? == *id {
|
||||
hwnds_to_purge.push((i, window.hwnd));
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Exe => {
|
||||
if window.exe()? == *id {
|
||||
hwnds_to_purge.push((i, window.hwnd));
|
||||
@@ -1384,10 +1389,14 @@ impl WindowManager {
|
||||
);
|
||||
}
|
||||
|
||||
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
|
||||
if stack != 0 && self.focused_container()?.windows().len() > 1 {
|
||||
BORDER_COLOUR_CURRENT
|
||||
.store(stack, Ordering::SeqCst);
|
||||
// it is not acceptable to fail here; we need to be able to send the event to
|
||||
// subscribers
|
||||
if self.focused_container().is_ok() {
|
||||
let stack = BORDER_COLOUR_STACK.load(Ordering::SeqCst);
|
||||
if stack != 0 && self.focused_container()?.windows().len() > 1 {
|
||||
BORDER_COLOUR_CURRENT
|
||||
.store(stack, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
|
||||
@@ -28,6 +28,8 @@ use crate::BORDER_COLOUR_STACK;
|
||||
use crate::BORDER_ENABLED;
|
||||
use crate::BORDER_HIDDEN;
|
||||
use crate::BORDER_HWND;
|
||||
use crate::BORDER_OFFSET;
|
||||
use crate::BORDER_WIDTH;
|
||||
use crate::DATA_DIR;
|
||||
use crate::HIDDEN_HWNDS;
|
||||
use crate::REGEX_IDENTIFIERS;
|
||||
@@ -185,6 +187,7 @@ impl WindowManager {
|
||||
let title = &window.title()?;
|
||||
let exe_name = &window.exe()?;
|
||||
let class = &window.class()?;
|
||||
let path = &window.path()?;
|
||||
|
||||
// We don't want to purge windows that have been deliberately hidden by us, eg. when
|
||||
// they are not on the top of a container stack.
|
||||
@@ -193,6 +196,7 @@ impl WindowManager {
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
path,
|
||||
&tray_and_multi_window_identifiers,
|
||||
®ex_identifiers,
|
||||
);
|
||||
@@ -376,7 +380,14 @@ impl WindowManager {
|
||||
|
||||
// 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;
|
||||
let right_bottom_constant = ((BORDER_WIDTH.load(Ordering::SeqCst)
|
||||
+ BORDER_OFFSET.load(Ordering::SeqCst))
|
||||
* 2)
|
||||
.abs();
|
||||
|
||||
let is_move = moved_across_monitors
|
||||
|| resize.right.abs() == right_bottom_constant
|
||||
&& resize.bottom.abs() == right_bottom_constant;
|
||||
|
||||
if is_move {
|
||||
tracing::info!("moving with mouse");
|
||||
@@ -468,17 +479,17 @@ impl WindowManager {
|
||||
let mut ops = vec![];
|
||||
|
||||
macro_rules! resize_op {
|
||||
($coordinate:expr, $comparator:tt, $direction:expr) => {{
|
||||
let adjusted = $coordinate * 2;
|
||||
let sizing = if adjusted $comparator 0 {
|
||||
Sizing::Decrease
|
||||
} else {
|
||||
Sizing::Increase
|
||||
};
|
||||
($coordinate:expr, $comparator:tt, $direction:expr) => {{
|
||||
let adjusted = $coordinate * 2;
|
||||
let sizing = if adjusted $comparator 0 {
|
||||
Sizing::Decrease
|
||||
} else {
|
||||
Sizing::Increase
|
||||
};
|
||||
|
||||
($direction, sizing, adjusted.abs())
|
||||
}};
|
||||
}
|
||||
($direction, sizing, adjusted.abs())
|
||||
}};
|
||||
}
|
||||
|
||||
if resize.left != 0 {
|
||||
ops.push(resize_op!(resize.left, >, OperationDirection::Left));
|
||||
@@ -488,11 +499,14 @@ impl WindowManager {
|
||||
ops.push(resize_op!(resize.top, >, OperationDirection::Up));
|
||||
}
|
||||
|
||||
if resize.right != 0 && resize.left == 0 {
|
||||
let top_left_constant = BORDER_WIDTH.load(Ordering::SeqCst)
|
||||
+ BORDER_OFFSET.load(Ordering::SeqCst);
|
||||
|
||||
if resize.right != 0 && resize.left == top_left_constant {
|
||||
ops.push(resize_op!(resize.right, <, OperationDirection::Right));
|
||||
}
|
||||
|
||||
if resize.bottom != 0 && resize.top == 0 {
|
||||
if resize.bottom != 0 && resize.top == top_left_constant {
|
||||
ops.push(resize_op!(resize.bottom, <, OperationDirection::Down));
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ use crate::WORKSPACE_RULES;
|
||||
|
||||
use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use hotwatch::notify::DebouncedEvent;
|
||||
use hotwatch::EventKind;
|
||||
use hotwatch::Hotwatch;
|
||||
use komorebi_core::config_generation::ApplicationConfigurationGenerator;
|
||||
use komorebi_core::config_generation::ApplicationOptions;
|
||||
@@ -744,10 +744,10 @@ impl StaticConfig {
|
||||
|
||||
let bytes = SocketMessage::ReloadStaticConfiguration(path.clone()).as_bytes()?;
|
||||
|
||||
wm.hotwatch.watch(path, move |event| match event {
|
||||
wm.hotwatch.watch(path, move |event| match event.kind {
|
||||
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends
|
||||
// a NoticeRemove, presumably because of the use of swap files?
|
||||
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
|
||||
EventKind::Modify(_) | EventKind::Remove(_) => {
|
||||
let socket = DATA_DIR.join("komorebi.sock");
|
||||
let mut stream =
|
||||
UnixStream::connect(socket).expect("could not connect to komorebi.sock");
|
||||
|
||||
@@ -377,6 +377,14 @@ impl Window {
|
||||
WindowsApi::window_text_w(self.hwnd())
|
||||
}
|
||||
|
||||
pub fn path(self) -> Result<String> {
|
||||
let (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd());
|
||||
let handle = WindowsApi::process_handle(process_id)?;
|
||||
let path = WindowsApi::exe_path(handle);
|
||||
WindowsApi::close_process(handle)?;
|
||||
path
|
||||
}
|
||||
|
||||
pub fn exe(self) -> Result<String> {
|
||||
let (process_id, _) = WindowsApi::window_thread_process_id(self.hwnd());
|
||||
let handle = WindowsApi::process_handle(process_id)?;
|
||||
@@ -440,8 +448,8 @@ impl Window {
|
||||
(true, _) |
|
||||
// If not allowing cloaked windows, we need to ensure the window is not cloaked
|
||||
(false, false) => {
|
||||
if let (Ok(title), Ok(exe_name), Ok(class)) = (self.title(), self.exe(), self.class()) {
|
||||
return Ok(window_is_eligible(&title, &exe_name, &class, &self.style()?, &self.ex_style()?, event));
|
||||
if let (Ok(title), Ok(exe_name), Ok(class), Ok(path)) = (self.title(), self.exe(), self.class(), self.path()) {
|
||||
return Ok(window_is_eligible(&title, &exe_name, &class, &path, &self.style()?, &self.ex_style()?, event));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -455,6 +463,7 @@ fn window_is_eligible(
|
||||
title: &String,
|
||||
exe_name: &String,
|
||||
class: &String,
|
||||
path: &str,
|
||||
style: &WindowStyle,
|
||||
ex_style: &ExtendedWindowStyle,
|
||||
event: Option<WindowManagerEvent>,
|
||||
@@ -473,6 +482,7 @@ fn window_is_eligible(
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
path,
|
||||
&float_identifiers,
|
||||
®ex_identifiers,
|
||||
);
|
||||
@@ -482,6 +492,7 @@ fn window_is_eligible(
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
path,
|
||||
&manage_identifiers,
|
||||
®ex_identifiers,
|
||||
);
|
||||
@@ -495,6 +506,7 @@ fn window_is_eligible(
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
path,
|
||||
&layered_whitelist,
|
||||
®ex_identifiers,
|
||||
);
|
||||
@@ -522,8 +534,13 @@ fn window_is_eligible(
|
||||
|| managed_override
|
||||
{
|
||||
return true;
|
||||
} else if event.is_some() {
|
||||
tracing::debug!("ignoring (exe: {}, title: {})", exe_name, title);
|
||||
} else if let Some(event) = event {
|
||||
tracing::debug!(
|
||||
"ignoring (exe: {}, title: {}, event: {})",
|
||||
exe_name,
|
||||
title,
|
||||
event
|
||||
);
|
||||
}
|
||||
|
||||
false
|
||||
@@ -534,6 +551,7 @@ pub fn should_act(
|
||||
title: &str,
|
||||
exe_name: &str,
|
||||
class: &str,
|
||||
path: &str,
|
||||
identifiers: &[IdWithIdentifier],
|
||||
regex_identifiers: &HashMap<String, Regex>,
|
||||
) -> bool {
|
||||
@@ -559,6 +577,11 @@ pub fn should_act(
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if path.eq(&identifier.id) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(MatchingStrategy::Equals) => match identifier.kind {
|
||||
ApplicationIdentifier::Title => {
|
||||
@@ -576,6 +599,11 @@ pub fn should_act(
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if path.eq(&identifier.id) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(MatchingStrategy::StartsWith) => match identifier.kind {
|
||||
ApplicationIdentifier::Title => {
|
||||
@@ -593,6 +621,11 @@ pub fn should_act(
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if path.starts_with(&identifier.id) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(MatchingStrategy::EndsWith) => match identifier.kind {
|
||||
ApplicationIdentifier::Title => {
|
||||
@@ -610,6 +643,11 @@ pub fn should_act(
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if path.ends_with(&identifier.id) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(MatchingStrategy::Contains) => match identifier.kind {
|
||||
ApplicationIdentifier::Title => {
|
||||
@@ -627,6 +665,11 @@ pub fn should_act(
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if path.contains(&identifier.id) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(MatchingStrategy::Regex) => match identifier.kind {
|
||||
ApplicationIdentifier::Title => {
|
||||
@@ -650,6 +693,13 @@ pub fn should_act(
|
||||
}
|
||||
}
|
||||
}
|
||||
ApplicationIdentifier::Path => {
|
||||
if let Some(re) = regex_identifiers.get(&identifier.id) {
|
||||
if re.is_match(path) {
|
||||
should_act = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ use color_eyre::eyre::anyhow;
|
||||
use color_eyre::eyre::bail;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use hotwatch::notify::DebouncedEvent;
|
||||
use hotwatch::notify::ErrorKind as NotifyErrorKind;
|
||||
use hotwatch::EventKind;
|
||||
use hotwatch::Hotwatch;
|
||||
use parking_lot::Mutex;
|
||||
use schemars::JsonSchema;
|
||||
@@ -211,12 +212,16 @@ impl WindowManager {
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn show_border(&self) -> Result<()> {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
if self.focused_container().is_ok() {
|
||||
let foreground = WindowsApi::foreground_window()?;
|
||||
let foreground_window = Window { hwnd: foreground };
|
||||
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, true)?;
|
||||
WindowsApi::invalidate_border_rect()
|
||||
let border = Border::from(BORDER_HWND.load(Ordering::SeqCst));
|
||||
border.set_position(foreground_window, true)?;
|
||||
WindowsApi::invalidate_border_rect()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
@@ -261,18 +266,18 @@ impl WindowManager {
|
||||
match self.hotwatch.unwatch(&config) {
|
||||
Ok(()) => {}
|
||||
Err(error) => match error {
|
||||
hotwatch::Error::Notify(error) => match error {
|
||||
hotwatch::notify::Error::WatchNotFound => {}
|
||||
error => return Err(error.into()),
|
||||
hotwatch::Error::Notify(ref notify_error) => match notify_error.kind {
|
||||
NotifyErrorKind::WatchNotFound => {}
|
||||
_ => return Err(error.into()),
|
||||
},
|
||||
error @ hotwatch::Error::Io(_) => return Err(error.into()),
|
||||
},
|
||||
}
|
||||
|
||||
self.hotwatch.watch(config, |event| match event {
|
||||
self.hotwatch.watch(config, |event| match event.kind {
|
||||
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends
|
||||
// a NoticeRemove, presumably because of the use of swap files?
|
||||
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
|
||||
EventKind::Modify(_) | EventKind::Remove(_) => {
|
||||
std::thread::spawn(|| {
|
||||
load_configuration().expect("could not load configuration");
|
||||
});
|
||||
@@ -851,17 +856,18 @@ impl WindowManager {
|
||||
}
|
||||
}
|
||||
|
||||
// if we passed false for follow_focus
|
||||
if !follow_focus
|
||||
// if we passed false for follow_focus and there is a container on the workspace
|
||||
if !follow_focus && self.focused_container_mut().is_ok() {
|
||||
// and we have a stack with >1 windows
|
||||
&& self.focused_container_mut()?.windows().len() > 1
|
||||
if self.focused_container_mut()?.windows().len() > 1
|
||||
// and we don't have a maxed window
|
||||
&& self.focused_workspace()?.maximized_window().is_none()
|
||||
// and we don't have a monocle container
|
||||
&& self.focused_workspace()?.monocle_container().is_none()
|
||||
{
|
||||
if let Ok(window) = self.focused_window_mut() {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
{
|
||||
if let Ok(window) = self.focused_window_mut() {
|
||||
window.focus(self.mouse_follows_focus)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1449,7 +1455,9 @@ impl WindowManager {
|
||||
anyhow!("this is not a valid direction from the current position")
|
||||
})?;
|
||||
|
||||
let adjusted_new_index = if new_idx > current_container_idx {
|
||||
let adjusted_new_index = if new_idx > current_container_idx
|
||||
&& !matches!(workspace.layout(), Layout::Default(DefaultLayout::Grid))
|
||||
{
|
||||
new_idx - 1
|
||||
} else {
|
||||
new_idx
|
||||
|
||||
@@ -139,11 +139,13 @@ impl WindowManagerEvent {
|
||||
let title = &window.title().ok()?;
|
||||
let exe_name = &window.exe().ok()?;
|
||||
let class = &window.class().ok()?;
|
||||
let path = &window.path().ok()?;
|
||||
|
||||
let should_trigger = should_act(
|
||||
title,
|
||||
exe_name,
|
||||
class,
|
||||
path,
|
||||
&object_name_change_on_launch,
|
||||
®ex_identifiers,
|
||||
);
|
||||
|
||||
@@ -54,8 +54,6 @@ use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
|
||||
use windows::Win32::System::Threading::PROCESS_NAME_WIN32;
|
||||
use windows::Win32::System::Threading::PROCESS_QUERY_INFORMATION;
|
||||
use windows::Win32::UI::HiDpi::GetDpiForMonitor;
|
||||
use windows::Win32::UI::HiDpi::GetDpiForSystem;
|
||||
use windows::Win32::UI::HiDpi::GetDpiForWindow;
|
||||
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||
use windows::Win32::UI::HiDpi::MDT_EFFECTIVE_DPI;
|
||||
@@ -359,7 +357,7 @@ impl WindowsApi {
|
||||
bottom: layout.bottom + shadow_rect.bottom,
|
||||
};
|
||||
|
||||
let position = if top { HWND_TOP } else { HWND_BOTTOM };
|
||||
let position = if top { HWND_TOP } else { HWND_NOTOPMOST };
|
||||
Self::set_window_pos(hwnd, &rect, position, flags.bits())
|
||||
}
|
||||
|
||||
@@ -499,9 +497,11 @@ impl WindowsApi {
|
||||
let mut rect = unsafe { std::mem::zeroed() };
|
||||
|
||||
if Self::dwm_get_window_attribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &mut rect).is_ok() {
|
||||
let window_scale = unsafe { GetDpiForWindow(hwnd) };
|
||||
let system_scale = unsafe { GetDpiForSystem() };
|
||||
Ok(Rect::from(rect).scale(system_scale.try_into()?, window_scale.try_into()?))
|
||||
// TODO(raggi): once we declare DPI awareness, we will need to scale the rect.
|
||||
// let window_scale = unsafe { GetDpiForWindow(hwnd) };
|
||||
// let system_scale = unsafe { GetDpiForSystem() };
|
||||
// Ok(Rect::from(rect).scale(system_scale.try_into()?, window_scale.try_into()?))
|
||||
Ok(Rect::from(rect))
|
||||
} else {
|
||||
unsafe { GetWindowRect(hwnd, &mut rect) }.process()?;
|
||||
Ok(Rect::from(rect))
|
||||
|
||||
@@ -39,6 +39,7 @@ use crate::ring::Ring;
|
||||
use crate::window::Window;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::winevent::WinEvent;
|
||||
use crate::winevent_listener;
|
||||
use crate::BORDER_COLOUR_CURRENT;
|
||||
use crate::BORDER_RECT;
|
||||
@@ -186,7 +187,10 @@ pub extern "system" fn win_event_hook(
|
||||
|
||||
let window = Window { hwnd: hwnd.0 };
|
||||
|
||||
let winevent = unsafe { ::std::mem::transmute(event) };
|
||||
let winevent = match WinEvent::try_from(event) {
|
||||
Ok(event) => event,
|
||||
Err(_) => return,
|
||||
};
|
||||
let event_type = match WindowManagerEvent::from_win_event(winevent, window) {
|
||||
None => return,
|
||||
Some(event) => event,
|
||||
|
||||
@@ -178,3 +178,100 @@ pub enum WinEvent {
|
||||
UiaPropIdSEnd = EVENT_UIA_PROPID_END,
|
||||
UiaPropIdStart = EVENT_UIA_PROPID_START,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for WinEvent {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
EVENT_AIA_END => Ok(Self::AiaEnd),
|
||||
EVENT_AIA_START => Ok(Self::AiaStart),
|
||||
EVENT_CONSOLE_CARET => Ok(Self::ConsoleCaret),
|
||||
EVENT_CONSOLE_END => Ok(Self::ConsoleEnd),
|
||||
EVENT_CONSOLE_END_APPLICATION => Ok(Self::ConsoleEndApplication),
|
||||
EVENT_CONSOLE_LAYOUT => Ok(Self::ConsoleLayout),
|
||||
EVENT_CONSOLE_START_APPLICATION => Ok(Self::ConsoleStartApplication),
|
||||
EVENT_CONSOLE_UPDATE_REGION => Ok(Self::ConsoleUpdateRegion),
|
||||
EVENT_CONSOLE_UPDATE_SCROLL => Ok(Self::ConsoleUpdateScroll),
|
||||
EVENT_CONSOLE_UPDATE_SIMPLE => Ok(Self::ConsoleUpdateSimple),
|
||||
EVENT_OBJECT_ACCELERATORCHANGE => Ok(Self::ObjectAcceleratorChange),
|
||||
EVENT_OBJECT_CLOAKED => Ok(Self::ObjectCloaked),
|
||||
EVENT_OBJECT_CONTENTSCROLLED => Ok(Self::ObjectContentScrolled),
|
||||
EVENT_OBJECT_CREATE => Ok(Self::ObjectCreate),
|
||||
EVENT_OBJECT_DEFACTIONCHANGE => Ok(Self::ObjectDefActionChange),
|
||||
EVENT_OBJECT_DESCRIPTIONCHANGE => Ok(Self::ObjectDescriptionChange),
|
||||
EVENT_OBJECT_DESTROY => Ok(Self::ObjectDestroy),
|
||||
EVENT_OBJECT_DRAGCANCEL => Ok(Self::ObjectDragCancel),
|
||||
EVENT_OBJECT_DRAGCOMPLETE => Ok(Self::ObjectDragComplete),
|
||||
EVENT_OBJECT_DRAGDROPPED => Ok(Self::ObjectDragDropped),
|
||||
EVENT_OBJECT_DRAGENTER => Ok(Self::ObjectDragEnter),
|
||||
EVENT_OBJECT_DRAGLEAVE => Ok(Self::ObjectDragLeave),
|
||||
EVENT_OBJECT_DRAGSTART => Ok(Self::ObjectDragStart),
|
||||
EVENT_OBJECT_END => Ok(Self::ObjectEnd),
|
||||
EVENT_OBJECT_FOCUS => Ok(Self::ObjectFocus),
|
||||
EVENT_OBJECT_HELPCHANGE => Ok(Self::ObjectHelpChange),
|
||||
EVENT_OBJECT_HIDE => Ok(Self::ObjectHide),
|
||||
EVENT_OBJECT_HOSTEDOBJECTSINVALIDATED => Ok(Self::ObjectHostedObjectsInvalidated),
|
||||
EVENT_OBJECT_IME_CHANGE => Ok(Self::ObjectImeChange),
|
||||
EVENT_OBJECT_IME_HIDE => Ok(Self::ObjectImeHide),
|
||||
EVENT_OBJECT_IME_SHOW => Ok(Self::ObjectImeShow),
|
||||
EVENT_OBJECT_INVOKED => Ok(Self::ObjectInvoked),
|
||||
EVENT_OBJECT_LIVEREGIONCHANGED => Ok(Self::ObjectLiveRegionChanged),
|
||||
EVENT_OBJECT_LOCATIONCHANGE => Ok(Self::ObjectLocationChange),
|
||||
EVENT_OBJECT_NAMECHANGE => Ok(Self::ObjectNameChange),
|
||||
EVENT_OBJECT_PARENTCHANGE => Ok(Self::ObjectParentChange),
|
||||
EVENT_OBJECT_REORDER => Ok(Self::ObjectReorder),
|
||||
EVENT_OBJECT_SELECTION => Ok(Self::ObjectSelection),
|
||||
EVENT_OBJECT_SELECTIONADD => Ok(Self::ObjectSelectionAdd),
|
||||
EVENT_OBJECT_SELECTIONREMOVE => Ok(Self::ObjectSelectionRemove),
|
||||
EVENT_OBJECT_SELECTIONWITHIN => Ok(Self::ObjectSelectionWithin),
|
||||
EVENT_OBJECT_SHOW => Ok(Self::ObjectShow),
|
||||
EVENT_OBJECT_STATECHANGE => Ok(Self::ObjectStateChange),
|
||||
EVENT_OBJECT_TEXTEDIT_CONVERSIONTARGETCHANGED => {
|
||||
Ok(Self::ObjectTextEditConversionTargetChanged)
|
||||
}
|
||||
EVENT_OBJECT_TEXTSELECTIONCHANGED => Ok(Self::ObjectTextSelectionChanged),
|
||||
EVENT_OBJECT_UNCLOAKED => Ok(Self::ObjectUncloaked),
|
||||
EVENT_OBJECT_VALUECHANGE => Ok(Self::ObjectValueChange),
|
||||
EVENT_OEM_DEFINED_END => Ok(Self::OemDefinedEnd),
|
||||
EVENT_OEM_DEFINED_START => Ok(Self::OemDefinedStart),
|
||||
EVENT_SYSTEM_ALERT => Ok(Self::SystemAlert),
|
||||
EVENT_SYSTEM_ARRANGMENTPREVIEW => Ok(Self::SystemArrangementPreview),
|
||||
EVENT_SYSTEM_CAPTUREEND => Ok(Self::SystemCaptureEnd),
|
||||
EVENT_SYSTEM_CAPTURESTART => Ok(Self::SystemCaptureStart),
|
||||
EVENT_SYSTEM_CONTEXTHELPEND => Ok(Self::SystemContextHelpEnd),
|
||||
EVENT_SYSTEM_CONTEXTHELPSTART => Ok(Self::SystemContextHelpStart),
|
||||
EVENT_SYSTEM_DESKTOPSWITCH => Ok(Self::SystemDesktopSwitch),
|
||||
EVENT_SYSTEM_DIALOGEND => Ok(Self::SystemDialogEnd),
|
||||
EVENT_SYSTEM_DIALOGSTART => Ok(Self::SystemDialogStart),
|
||||
EVENT_SYSTEM_DRAGDROPEND => Ok(Self::SystemDragDropEnd),
|
||||
EVENT_SYSTEM_DRAGDROPSTART => Ok(Self::SystemDragDropStart),
|
||||
EVENT_SYSTEM_END => Ok(Self::SystemEnd),
|
||||
EVENT_SYSTEM_FOREGROUND => Ok(Self::SystemForeground),
|
||||
EVENT_SYSTEM_IME_KEY_NOTIFICATION => Ok(Self::SystemImeKeyNotification),
|
||||
EVENT_SYSTEM_MENUEND => Ok(Self::SystemMenuEnd),
|
||||
EVENT_SYSTEM_MENUPOPUPEND => Ok(Self::SystemMenuPopupEnd),
|
||||
EVENT_SYSTEM_MENUPOPUPSTART => Ok(Self::SystemMenuPopupStart),
|
||||
EVENT_SYSTEM_MENUSTART => Ok(Self::SystemMenuStart),
|
||||
EVENT_SYSTEM_MINIMIZEEND => Ok(Self::SystemMinimizeEnd),
|
||||
EVENT_SYSTEM_MINIMIZESTART => Ok(Self::SystemMinimizeStart),
|
||||
EVENT_SYSTEM_MOVESIZEEND => Ok(Self::SystemMoveSizeEnd),
|
||||
EVENT_SYSTEM_MOVESIZESTART => Ok(Self::SystemMoveSizeStart),
|
||||
EVENT_SYSTEM_SCROLLINGEND => Ok(Self::SystemScrollingEnd),
|
||||
EVENT_SYSTEM_SCROLLINGSTART => Ok(Self::SystemScrollingStart),
|
||||
EVENT_SYSTEM_SOUND => Ok(Self::SystemSound),
|
||||
EVENT_SYSTEM_SWITCHEND => Ok(Self::SystemSwitchEnd),
|
||||
EVENT_SYSTEM_SWITCHER_APPDROPPED => Ok(Self::SystemSwitcherAppDropped),
|
||||
EVENT_SYSTEM_SWITCHER_APPGRABBED => Ok(Self::SystemSwitcherAppGrabbed),
|
||||
EVENT_SYSTEM_SWITCHER_APPOVERTARGET => Ok(Self::SystemSwitcherAppOverTarget),
|
||||
EVENT_SYSTEM_SWITCHER_CANCELLED => Ok(Self::SystemSwitcherCancelled),
|
||||
EVENT_SYSTEM_SWITCHSTART => Ok(Self::SystemSwitchStart),
|
||||
EVENT_UIA_EVENTID_END => Ok(Self::UiaEventIdSEnd),
|
||||
EVENT_UIA_EVENTID_START => Ok(Self::UiaEventIdStart),
|
||||
EVENT_UIA_PROPID_END => Ok(Self::UiaPropIdSEnd),
|
||||
EVENT_UIA_PROPID_START => Ok(Self::UiaPropIdStart),
|
||||
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic-no-console"
|
||||
version = "0.1.22-dev.0"
|
||||
version = "0.1.22"
|
||||
authors = ["Jade Iqbal <jadeiqbal@fastmail.com>"]
|
||||
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
|
||||
categories = ["cli", "tiling-window-manager", "windows"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebic"
|
||||
version = "0.1.22-dev.0"
|
||||
version = "0.1.22"
|
||||
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"]
|
||||
@@ -21,12 +21,12 @@ dunce = { workspace = true }
|
||||
fs-tail = "0.1"
|
||||
heck = "0.4"
|
||||
lazy_static = "1"
|
||||
miette = { version = "5", features = ["fancy"] }
|
||||
miette = { version = "7", features = ["fancy"] }
|
||||
paste = "1"
|
||||
powershell_script = "1.0"
|
||||
reqwest = { version = "0.11", features = ["blocking"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = "0.9"
|
||||
sysinfo = "0.30"
|
||||
thiserror = "1"
|
||||
|
||||
@@ -114,7 +114,7 @@ trait AhkFunction {
|
||||
struct ConfigurationError {
|
||||
message: String,
|
||||
#[source_code]
|
||||
src: NamedSource,
|
||||
src: NamedSource<String>,
|
||||
#[label("This bit here")]
|
||||
bad_bit: SourceSpan,
|
||||
}
|
||||
@@ -1367,7 +1367,7 @@ fn main() -> Result<()> {
|
||||
let diagnostic = ConfigurationError {
|
||||
message: msgs[0].to_string(),
|
||||
src: NamedSource::new("komorebi.json", config_source.clone()),
|
||||
bad_bit: SourceSpan::new(offset, 2.into()),
|
||||
bad_bit: SourceSpan::new(offset, 2),
|
||||
};
|
||||
|
||||
println!("{:?}", Report::new(diagnostic));
|
||||
|
||||
11
mkdocs.yml
11
mkdocs.yml
@@ -62,17 +62,23 @@ nav:
|
||||
- common-workflows/custom-layouts.md
|
||||
- common-workflows/dynamic-layout-switching.md
|
||||
# - common-workflows/autohotkey.md
|
||||
- Release notes:
|
||||
- release/v0-1-22.md
|
||||
- Configuration reference: https://komorebi.lgug2z.com/schema
|
||||
- CLI reference:
|
||||
- cli/quickstart.md
|
||||
- cli/start.md
|
||||
- cli/stop.md
|
||||
- cli/check.md
|
||||
- cli/configuration.md
|
||||
- cli/whkdrc.md
|
||||
- cli/state.md
|
||||
- cli/visible-windows.md
|
||||
- cli/query.md
|
||||
- cli/subscribe.md
|
||||
- cli/unsubscribe.md
|
||||
- cli/subscribe-socket.md
|
||||
- cli/unsubscribe-socket.md
|
||||
- cli/subscribe-pipe.md
|
||||
- cli/unsubscribe-pipe.md
|
||||
- cli/log.md
|
||||
- cli/quick-save-resize.md
|
||||
- cli/quick-load-resize.md
|
||||
@@ -160,7 +166,6 @@ nav:
|
||||
- cli/reload-configuration.md
|
||||
- cli/watch-configuration.md
|
||||
- cli/complete-configuration.md
|
||||
- cli/alt-focus-hack.md
|
||||
- cli/window-hiding-behaviour.md
|
||||
- cli/cross-monitor-move-behaviour.md
|
||||
- cli/toggle-cross-monitor-move-behaviour.md
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"ApplicationOptions": {
|
||||
|
||||
189
schema.json
189
schema.json
@@ -133,20 +133,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"active_window_border_offset": {
|
||||
"description": "Offset of the active window border (default: -1)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"active_window_border_width": {
|
||||
"description": "Width of the active window border (default: 8)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"app_specific_configuration_path": {
|
||||
"description": "Path to applications.yaml from komorebi-application-specific-configurations (default: None)",
|
||||
"type": "string"
|
||||
},
|
||||
"border_offset": {
|
||||
"description": "Offset of the window border (default: -1)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"border_overflow_applications": {
|
||||
"description": "Identify border overflow applications",
|
||||
"type": "array",
|
||||
@@ -165,7 +160,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -182,6 +178,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"border_width": {
|
||||
"description": "Width of the window border (default: 8)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"cross_monitor_move_behaviour": {
|
||||
"description": "Determine what happens when a window is moved across a monitor boundary (default: Swap)",
|
||||
"oneOf": [
|
||||
@@ -236,7 +237,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -305,7 +307,7 @@
|
||||
}
|
||||
},
|
||||
"invisible_borders": {
|
||||
"description": "Dimensions of Windows' own invisible borders; don't set these yourself unless you are told to",
|
||||
"description": "DEPRECATED from v0.1.22: no longer required",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"bottom",
|
||||
@@ -354,7 +356,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -389,7 +392,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -525,7 +529,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -551,7 +556,8 @@
|
||||
"Rows",
|
||||
"VerticalStack",
|
||||
"HorizontalStack",
|
||||
"UltrawideVerticalStack"
|
||||
"UltrawideVerticalStack",
|
||||
"Grid"
|
||||
]
|
||||
},
|
||||
"layout_rules": {
|
||||
@@ -565,7 +571,8 @@
|
||||
"Rows",
|
||||
"VerticalStack",
|
||||
"HorizontalStack",
|
||||
"UltrawideVerticalStack"
|
||||
"UltrawideVerticalStack",
|
||||
"Grid"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -596,7 +603,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -641,7 +649,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
@@ -663,6 +672,143 @@
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"stackbar": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"height": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Always",
|
||||
"Never",
|
||||
"OnStack"
|
||||
]
|
||||
},
|
||||
"tabs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"background": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Colour represented as RGB",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"b",
|
||||
"g",
|
||||
"r"
|
||||
],
|
||||
"properties": {
|
||||
"b": {
|
||||
"description": "Blue",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"g": {
|
||||
"description": "Green",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"r": {
|
||||
"description": "Red",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Colour represented as Hex",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"focused_text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Colour represented as RGB",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"b",
|
||||
"g",
|
||||
"r"
|
||||
],
|
||||
"properties": {
|
||||
"b": {
|
||||
"description": "Blue",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"g": {
|
||||
"description": "Green",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"r": {
|
||||
"description": "Red",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Colour represented as Hex",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"unfocused_text": {
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "Colour represented as RGB",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"b",
|
||||
"g",
|
||||
"r"
|
||||
],
|
||||
"properties": {
|
||||
"b": {
|
||||
"description": "Blue",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"g": {
|
||||
"description": "Green",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"r": {
|
||||
"description": "Red",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Colour represented as Hex",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"width": {
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tray_and_multi_window_applications": {
|
||||
"description": "Identify tray and multi-window applications",
|
||||
"type": "array",
|
||||
@@ -681,7 +827,8 @@
|
||||
"enum": [
|
||||
"Exe",
|
||||
"Class",
|
||||
"Title"
|
||||
"Title",
|
||||
"Path"
|
||||
]
|
||||
},
|
||||
"matching_strategy": {
|
||||
|
||||
Reference in New Issue
Block a user