mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-14 14:23:36 +01:00
Compare commits
299 Commits
feature/no
...
feature/aa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1834f092d7 | ||
|
|
aa5a36989f | ||
|
|
b612066367 | ||
|
|
cbe5b24f73 | ||
|
|
2ead216eeb | ||
|
|
b0944662fa | ||
|
|
1376d7be04 | ||
|
|
5da72e10df | ||
|
|
d21ffb28cc | ||
|
|
4e27febdc0 | ||
|
|
3c5852ae20 | ||
|
|
7943fccb1b | ||
|
|
2d2cea31c0 | ||
|
|
a17924c2af | ||
|
|
91519227d4 | ||
|
|
f91d0aabf5 | ||
|
|
d5b6584042 | ||
|
|
6db317d425 | ||
|
|
07a1538905 | ||
|
|
853db2f15f | ||
|
|
c9f180ce0f | ||
|
|
bc00f54c90 | ||
|
|
c57759242a | ||
|
|
6f27de8e5c | ||
|
|
a9e98034b0 | ||
|
|
3489163793 | ||
|
|
71d65cf4a1 | ||
|
|
a5e6828d1e | ||
|
|
96605f72a7 | ||
|
|
18bb060b71 | ||
|
|
20f370a51d | ||
|
|
2d1613b4d9 | ||
|
|
216154b975 | ||
|
|
fe9b7e53b7 | ||
|
|
d9bffa06df | ||
|
|
d34363af8f | ||
|
|
db4b0dd63b | ||
|
|
7b563aac5e | ||
|
|
b7198242ff | ||
|
|
a1f1be0afe | ||
|
|
e4b7adeb0f | ||
|
|
818ac3404c | ||
|
|
d6ae81af13 | ||
|
|
109227b74c | ||
|
|
ddb600f745 | ||
|
|
167ec92811 | ||
|
|
d110e12a62 | ||
|
|
21bd09e419 | ||
|
|
01ccf70ad5 | ||
|
|
c3f135703e | ||
|
|
3720ce42d0 | ||
|
|
1080159e68 | ||
|
|
360d0915a1 | ||
|
|
182c1e6a96 | ||
|
|
14d2ebd756 | ||
|
|
50b89cc1df | ||
|
|
df409902bb | ||
|
|
df19d06333 | ||
|
|
96d094d9d7 | ||
|
|
2916256e79 | ||
|
|
21a2138330 | ||
|
|
6addfed1ce | ||
|
|
6ba0ba79f9 | ||
|
|
254fcc988f | ||
|
|
7005a01d9c | ||
|
|
de3d4d0d99 | ||
|
|
b69db863f1 | ||
|
|
286bb0070c | ||
|
|
45894be4ff | ||
|
|
bc67936dd3 | ||
|
|
c06d9afa3b | ||
|
|
b799fd3077 | ||
|
|
3c03528750 | ||
|
|
821a124771 | ||
|
|
8f7b9202b2 | ||
|
|
13e2cbc7a1 | ||
|
|
ff653e78af | ||
|
|
6d038b8b18 | ||
|
|
45a5941872 | ||
|
|
f54097f094 | ||
|
|
29b14f8dc8 | ||
|
|
a1cf5ba29c | ||
|
|
a60e5a77c2 | ||
|
|
f722905be1 | ||
|
|
b5eafc6b96 | ||
|
|
c367967301 | ||
|
|
780635c8ef | ||
|
|
d5bec7afa4 | ||
|
|
974aa0d000 | ||
|
|
0f9c23b6f4 | ||
|
|
6ea71834a1 | ||
|
|
81451cb17a | ||
|
|
7653495e31 | ||
|
|
0cdce8fc2a | ||
|
|
67f14730d0 | ||
|
|
ef9e734680 | ||
|
|
faa7786979 | ||
|
|
3c8a6cb7bd | ||
|
|
50a279239a | ||
|
|
2c8f25ef82 | ||
|
|
bdc1cad597 | ||
|
|
e2f2d6b919 | ||
|
|
aebe8792ac | ||
|
|
9fb6f8ebcd | ||
|
|
6eb6129618 | ||
|
|
5b997b6ea6 | ||
|
|
9414466646 | ||
|
|
9e87baa8b8 | ||
|
|
2a67c9c786 | ||
|
|
60bc96e9a5 | ||
|
|
5e9d573f0b | ||
|
|
128db85054 | ||
|
|
cc7dbde049 | ||
|
|
a5735c4186 | ||
|
|
48cb3db2fe | ||
|
|
24bff7e527 | ||
|
|
b985659e30 | ||
|
|
1d0ac9b555 | ||
|
|
7b1ece9680 | ||
|
|
00fc5382f6 | ||
|
|
9e37baa88a | ||
|
|
9a65a4ae92 | ||
|
|
a511cbd263 | ||
|
|
83cc7bf7c0 | ||
|
|
8bf4ab9f15 | ||
|
|
31864b1570 | ||
|
|
c022438a37 | ||
|
|
3d518f73ca | ||
|
|
d2d6484e38 | ||
|
|
67a3c3546f | ||
|
|
888b674646 | ||
|
|
5abab46290 | ||
|
|
ad8375eebe | ||
|
|
a11da2167c | ||
|
|
6b9a0843fd | ||
|
|
41732e2f5f | ||
|
|
9a58c1ee42 | ||
|
|
edc87d9940 | ||
|
|
133311bbe2 | ||
|
|
280aebf15d | ||
|
|
a488890a04 | ||
|
|
9a0ee8e8dd | ||
|
|
f23510055a | ||
|
|
a5fb5527c6 | ||
|
|
3f6e19b8b4 | ||
|
|
aa24c41967 | ||
|
|
1320b7440e | ||
|
|
cad2eb9a63 | ||
|
|
b7a987be8f | ||
|
|
e8f6a66bed | ||
|
|
fd97c7230d | ||
|
|
cc60f55cec | ||
|
|
270374497c | ||
|
|
6c90001c00 | ||
|
|
3232d9242a | ||
|
|
e57b08d073 | ||
|
|
cfb0c7f2ce | ||
|
|
5cff90a62b | ||
|
|
da7a9394d8 | ||
|
|
88684f991f | ||
|
|
03fdbea5cd | ||
|
|
340c137342 | ||
|
|
e46a0757e3 | ||
|
|
3556f38469 | ||
|
|
62770033f2 | ||
|
|
e294dbbe93 | ||
|
|
47f0ab1ef3 | ||
|
|
c4d62fc4f6 | ||
|
|
69680b4238 | ||
|
|
0dc17e9cb3 | ||
|
|
0f44efaa82 | ||
|
|
05af7ce16a | ||
|
|
92447723d2 | ||
|
|
2a45f981e6 | ||
|
|
a29ab4cfb3 | ||
|
|
27cd1736aa | ||
|
|
d2470b1f08 | ||
|
|
835472d739 | ||
|
|
bceb28de37 | ||
|
|
fff7b5c147 | ||
|
|
1e63947ae3 | ||
|
|
d5f4f916be | ||
|
|
77fc3973b6 | ||
|
|
29b1794409 | ||
|
|
1420334c94 | ||
|
|
82aa2edf8f | ||
|
|
228cb26b64 | ||
|
|
3d53c602a7 | ||
|
|
81f741bbbd | ||
|
|
0330dfe250 | ||
|
|
70ef90b304 | ||
|
|
d102c00ffe | ||
|
|
87b1ab9c53 | ||
|
|
a4dd5fc741 | ||
|
|
226ee73aa4 | ||
|
|
e14235c3a9 | ||
|
|
855bb49804 | ||
|
|
07b2da69a1 | ||
|
|
6a1ed3bcaa | ||
|
|
1b30561989 | ||
|
|
c47cf4718b | ||
|
|
1accbf65ca | ||
|
|
7ee3c928d8 | ||
|
|
6d1903099a | ||
|
|
d5c6f090cc | ||
|
|
598f9ec0aa | ||
|
|
11acff5236 | ||
|
|
4802b55452 | ||
|
|
482a7b1d7f | ||
|
|
d00ee82a9d | ||
|
|
9f01d8fa0f | ||
|
|
627088c9b9 | ||
|
|
22cf7b5017 | ||
|
|
3e984d886c | ||
|
|
185cb4d4a8 | ||
|
|
62900c59cb | ||
|
|
a2e9a46582 | ||
|
|
871a53821c | ||
|
|
bcd1c50d82 | ||
|
|
0c41d9ded2 | ||
|
|
4af62fe97b | ||
|
|
7cab062124 | ||
|
|
611fa34567 | ||
|
|
95990d682b | ||
|
|
e363a494c3 | ||
|
|
383533e2d9 | ||
|
|
0b04e3ef93 | ||
|
|
3370e6acc5 | ||
|
|
4ffffc5eec | ||
|
|
2b5f737d14 | ||
|
|
8a455c8ab7 | ||
|
|
3d9871c576 | ||
|
|
cafb8e9a48 | ||
|
|
740cb3c877 | ||
|
|
b8b3b3d615 | ||
|
|
6fce630be5 | ||
|
|
dfd0d604aa | ||
|
|
aff1081ccd | ||
|
|
793e81d43d | ||
|
|
eac4c8e9b1 | ||
|
|
6b42587af4 | ||
|
|
46152621c0 | ||
|
|
b78693118b | ||
|
|
7caa839a00 | ||
|
|
1671f31e3e | ||
|
|
6fe46610fc | ||
|
|
4ba3125dde | ||
|
|
efa562de5c | ||
|
|
b476bee1d8 | ||
|
|
86b07f28dd | ||
|
|
311e37c8a2 | ||
|
|
15c3b32608 | ||
|
|
28b46c54da | ||
|
|
732aca77b5 | ||
|
|
f56fc36557 | ||
|
|
5334e1944e | ||
|
|
d8d087e621 | ||
|
|
b61146ead4 | ||
|
|
16cb811aa9 | ||
|
|
862219b9a3 | ||
|
|
0fc75afb00 | ||
|
|
d67f355a17 | ||
|
|
3d0ed4cfc4 | ||
|
|
21be01b9aa | ||
|
|
648ba672e3 | ||
|
|
af6529851e | ||
|
|
bea3d1fcbc | ||
|
|
e40fa11b0d | ||
|
|
251ec3d53e | ||
|
|
5e714cafab | ||
|
|
6aa9be1ea0 | ||
|
|
6238d1f848 | ||
|
|
d83dc48230 | ||
|
|
f2c4dadcde | ||
|
|
c05e9ea089 | ||
|
|
37aa99a537 | ||
|
|
dca32bead5 | ||
|
|
77eaddca1e | ||
|
|
79fb098d04 | ||
|
|
d0bab4280a | ||
|
|
e616004da7 | ||
|
|
c65060fbd9 | ||
|
|
50a851a660 | ||
|
|
8ad6f4be1f | ||
|
|
24ac0c4cd3 | ||
|
|
e0652d953a | ||
|
|
d6e0ecc507 | ||
|
|
ca22cdb07f | ||
|
|
dc38eae2af | ||
|
|
66446f571c | ||
|
|
b2f6329963 | ||
|
|
bc46f65f64 | ||
|
|
69573c383f | ||
|
|
45a3f2a6b5 | ||
|
|
5af00b64cf | ||
|
|
81dff3279c | ||
|
|
2f17e4bb29 | ||
|
|
1b966d3731 | ||
|
|
e58d776f81 |
52
.github/ISSUE_TEMPLATE/bug_report.md
vendored
52
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,52 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: "[BUG]: Short descriptive title"
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See bug
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots and Videos**
|
||||
Add screenshots and videos to help explain your problem.
|
||||
|
||||
**Operating System**
|
||||
Provide the output of `systeminfo | grep "^OS Name\|^OS Version"`
|
||||
|
||||
For example:
|
||||
```
|
||||
OS Name: Microsoft Windows 11 Pro
|
||||
OS Version: 10.0.22000 N/A Build 22000
|
||||
```
|
||||
|
||||
**`komorebic check` Output**
|
||||
Provide the output of `komorebic check`
|
||||
|
||||
For example:
|
||||
```
|
||||
No KOMOREBI_CONFIG_HOME detected, defaulting to C:\Users\LGUG2Z
|
||||
|
||||
Looking for configuration files in C:\Users\LGUG2Z
|
||||
|
||||
No komorebi configuration found in C:\Users\LGUG2Z
|
||||
|
||||
If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
In particular, if you have any other AHK scripts or software running that handle any aspect of window management or manipulation
|
||||
56
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
56
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Bug report
|
||||
description: File a bug report
|
||||
labels: [bug]
|
||||
title: "[BUG]: "
|
||||
body:
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Summary
|
||||
description: >
|
||||
Please provide a short summary of the bug, along with any information
|
||||
you feel is relevant to replicating the bug.
|
||||
|
||||
You may include screenshots and videos in this section.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Version Information
|
||||
description: >
|
||||
Please provide information about the versions of Windows and komorebi
|
||||
running on your machine.
|
||||
|
||||
Do not submit a bug if you are not using an official version of Windows
|
||||
such as AtlasOS; only official versions of Windows are supported.
|
||||
|
||||
```
|
||||
systeminfo | findstr /B /C:"OS Name" /B /C:"OS Version"
|
||||
```
|
||||
|
||||
```
|
||||
komorebic --version
|
||||
```
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Komorebi Configuration
|
||||
description: >
|
||||
Please provide your configuration file (komorebi.json or komorebi.bar.json)
|
||||
render: json
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Hotkey Configuration
|
||||
description: >
|
||||
Please provide your whkdrc or komorebi.ahk hotkey configuration file
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Output of komorebic check
|
||||
description: >
|
||||
Please provide the output of `komorebic check`
|
||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Komorebi Documentation
|
||||
url: https://lgug2z.github.io/komorebi/
|
||||
about: Please search the documentation website before opening an issue
|
||||
- name: Komorebi Quickstart Tutorial Video
|
||||
url: https://www.youtube.com/watch?v=MMZUAtHbTYY
|
||||
about: If you are new, please make sure you watch the quickstart tutorial video
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEAT]: Short descriptive title"
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
37
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
37
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Feature request
|
||||
description: Suggest a new feature
|
||||
labels: [enhancement]
|
||||
title: "[FEAT]: "
|
||||
body:
|
||||
- type: dropdown
|
||||
id: Sponsors
|
||||
attributes:
|
||||
label: Sponsorship Information
|
||||
description: >
|
||||
Feature requests are considered from individuals who are $5+ monthly sponsors to the project.
|
||||
|
||||
Please specify the platform you use to sponsor the project.
|
||||
options:
|
||||
- GitHub Sponsors
|
||||
- Ko-fi
|
||||
- Discord
|
||||
- YouTube
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Suggestion
|
||||
description: >
|
||||
Please share your suggestion here. Be sure to include all necessary context.
|
||||
|
||||
If you sponsor on a platform where you use a different username, please specify the username here.
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: >
|
||||
Please share share alternatives you have considered here.
|
||||
248
.github/workflows/windows.yaml
vendored
248
.github/workflows/windows.yaml
vendored
@@ -14,124 +14,184 @@ on:
|
||||
tags:
|
||||
- v*
|
||||
schedule:
|
||||
- cron: "30 0 * * 1" # Every Monday at half past midnight UTC
|
||||
- cron: "30 0 * * 0" # Every day at 00:30 UTC
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
strategy:
|
||||
fail-fast: false
|
||||
fail-fast: true
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-pc-windows-msvc
|
||||
platform:
|
||||
- os-name: Windows-x86_64
|
||||
runs-on: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
- os-name: Windows-aarch64
|
||||
runs-on: windows-latest
|
||||
target: aarch64-pc-windows-msvc
|
||||
runs-on: ${{ matrix.platform.runs-on }}
|
||||
permissions: write-all
|
||||
env:
|
||||
RUSTFLAGS: -Ctarget-feature=+crt-static -Dwarnings
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Prep cargo dirs
|
||||
run: |
|
||||
New-Item "${env:USERPROFILE}\.cargo\registry" -ItemType Directory -Force
|
||||
New-Item "${env:USERPROFILE}\.cargo\git" -ItemType Directory -Force
|
||||
shell: powershell
|
||||
- name: Set environment variables appropriately for the build
|
||||
run: |
|
||||
echo "%USERPROFILE%\.cargo\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
|
||||
echo "TARGET=${{ matrix.target }}" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
||||
echo "SKIP_TESTS=" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
||||
- name: Cache cargo registry, git trees and binaries
|
||||
uses: actions/cache@v4
|
||||
- run: rustup toolchain install stable --profile minimal
|
||||
- run: rustup toolchain install nightly --allow-downgrade -c rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
~/.cargo/bin
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Get rustc commit hash
|
||||
id: cargo-target-cache
|
||||
run: |
|
||||
echo "::set-output name=rust_hash::$(rustc -Vv | grep commit-hash | awk '{print $2}')"
|
||||
shell: bash
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v4
|
||||
cache-on-failure: "true"
|
||||
cache-all-crates: "true"
|
||||
- run: cargo +nightly fmt --check
|
||||
- run: cargo clippy
|
||||
- uses: houseabsolute/actions-rust-cross@v0
|
||||
with:
|
||||
path: target
|
||||
key: ${{ github.base_ref }}-${{ github.head_ref }}-${{ matrix.target }}-cargo-target-dir-${{ steps.cargo-target-cache.outputs.rust_hash }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: ${{ github.base_ref }}-${{ matrix.target }}-cargo-target-dir-${{ steps.cargo-target-cache.outputs.rust_hash }}-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Install Rustup using win.rustup.rs
|
||||
run: |
|
||||
# Disable the download progress bar which can cause perf issues
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
Invoke-WebRequest https://win.rustup.rs/ -OutFile rustup-init.exe
|
||||
.\rustup-init.exe -y --default-host=x86_64-pc-windows-msvc --profile=minimal
|
||||
shell: powershell
|
||||
- name: Ensure stable toolchain is up to date
|
||||
run: rustup update stable
|
||||
shell: bash
|
||||
- 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 }}
|
||||
- name: Create MSI installer
|
||||
run: |
|
||||
command: "build"
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: "--locked --release"
|
||||
- run: |
|
||||
cargo install cargo-wix
|
||||
cargo wix -p komorebi --nocapture -I .\wix\main.wxs --target x86_64-pc-windows-msvc
|
||||
- name: Upload the built artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
cargo wix --no-build -p komorebi --nocapture -I .\wix\main.wxs --target ${{ matrix.platform.target }}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: komorebi-${{ matrix.target }}
|
||||
name: komorebi-${{ matrix.platform.target }}-${{ github.sha }}
|
||||
path: |
|
||||
target/${{ matrix.target }}/release/komorebi.exe
|
||||
target/${{ matrix.target }}/release/komorebic.exe
|
||||
target/${{ matrix.target }}/release/komorebic-no-console.exe
|
||||
target/${{ matrix.target }}/release/komorebi.pdb
|
||||
target/${{ matrix.target }}/release/komorebic.pdb
|
||||
target/${{ matrix.target }}/release/komorebic-no-console.pdb
|
||||
target/${{ matrix.platform.target }}/release/*.exe
|
||||
target/${{ matrix.platform.target }}/release/*.pdb
|
||||
target/wix/komorebi-*.msi
|
||||
retention-days: 7
|
||||
- name: Check GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
version: latest
|
||||
args: build --skip=validate --clean
|
||||
retention-days: 14
|
||||
|
||||
# Release
|
||||
- name: Generate changelog
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
shell: bash
|
||||
nightly:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
permissions: write-all
|
||||
if: ${{ github.ref == 'refs/heads/master' && github.event_name == 'schedule' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- shell: bash
|
||||
run: echo "VERSION=nightly" >> $GITHUB_ENV
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: |
|
||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip" >checksums.txt
|
||||
|
||||
Compress-Archive -Force ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/aarch64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/wix/*aarch64.msi -Destination ./komorebi-$Env:VERSION-aarch64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip" >>checksums.txt
|
||||
- shell: bash
|
||||
run: |
|
||||
if ! type kokai >/dev/null; then cargo install --locked kokai --force; fi
|
||||
git tag -d nightly || true
|
||||
git tag nightly
|
||||
kokai release --no-emoji --add-links github:commits,issues --ref nightly >"CHANGELOG.md"
|
||||
- shell: bash
|
||||
run: |
|
||||
gh release delete nightly --yes || true
|
||||
git push origin :nightly || true
|
||||
gh release create nightly \
|
||||
--target $GITHUB_SHA \
|
||||
--prerelease \
|
||||
--title "komorebi nightly (${GITHUB_SHA})" \
|
||||
--notes-file CHANGELOG.md
|
||||
komorebi-nightly-x86_64-pc-windows-msvc.zip \
|
||||
komorebi-nightly-x86_64.msi \
|
||||
komorebi-nightly-aarch64-pc-windows-msvc.zip \
|
||||
komorebi-nightly-aarch64.msi \
|
||||
checksums.txt
|
||||
|
||||
release-dry-run:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
permissions: write-all
|
||||
if: ${{ github.ref == 'refs/heads/master' }}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- shell: bash
|
||||
run: |
|
||||
TAG=${{ github.event.release.tag_name }}
|
||||
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: |
|
||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip" >checksums.txt
|
||||
|
||||
Compress-Archive -Force ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/aarch64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/wix/*aarch64.msi -Destination ./komorebi-$Env:VERSION-aarch64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip" >>checksums.txt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: "true"
|
||||
cache-all-crates: "true"
|
||||
- shell: bash
|
||||
run: |
|
||||
if ! type kokai >/dev/null; then cargo install --locked kokai --force; fi
|
||||
git tag -d nightly || true
|
||||
kokai release --no-emoji --add-links github:commits,issues --ref "${{ github.ref_name }}" >"CHANGELOG.md"
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
body_path: "CHANGELOG.md"
|
||||
files: |
|
||||
checksums.txt
|
||||
*.zip
|
||||
*.msi
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: windows-latest
|
||||
permissions: write-all
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- shell: bash
|
||||
run: |
|
||||
TAG=${{ github.event.release.tag_name }}
|
||||
echo "VERSION=${TAG#v}" >> $GITHUB_ENV
|
||||
- uses: actions/download-artifact@v4
|
||||
- run: |
|
||||
Compress-Archive -Force ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/x86_64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-x86_64-pc-windows-msvc-${{ github.sha }}/wix/*x86_64.msi -Destination ./komorebi-$Env:VERSION-x86_64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-x86_64-pc-windows-msvc.zip" >checksums.txt
|
||||
|
||||
Compress-Archive -Force ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/aarch64-pc-windows-msvc/release/*.exe komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip
|
||||
Copy-Item ./komorebi-aarch64-pc-windows-msvc-${{ github.sha }}/wix/*aarch64.msi -Destination ./komorebi-$Env:VERSION-aarch64.msi
|
||||
echo "$((Get-FileHash komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip).Hash.ToLower()) komorebi-$Env:VERSION-aarch64-pc-windows-msvc.zip" >>checksums.txt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: "true"
|
||||
cache-all-crates: "true"
|
||||
- shell: bash
|
||||
run: |
|
||||
if ! type kokai >/dev/null; then cargo install --locked kokai --force; fi
|
||||
git tag -d nightly || true
|
||||
kokai release --no-emoji --add-links github:commits,issues --ref "$(git tag --points-at HEAD)" >"CHANGELOG.md"
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
version: latest
|
||||
args: release --skip=validate --clean --release-notes=CHANGELOG.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SCOOP_TOKEN: ${{ secrets.SCOOP_TOKEN }}
|
||||
- name: Add MSI to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
files: "target/wix/komorebi-*.msi"
|
||||
body_path: "CHANGELOG.md"
|
||||
files: |
|
||||
checksums.txt
|
||||
*.zip
|
||||
*.msi
|
||||
|
||||
winget:
|
||||
name: Publish to WinGet
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
needs: release
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,4 +3,5 @@
|
||||
/target
|
||||
CHANGELOG.md
|
||||
dummy.go
|
||||
komorebi.ahk
|
||||
komorebic/applications.yaml
|
||||
/.vs
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# Adapted from https://jondot.medium.com/shipping-rust-binaries-with-goreleaser-d5aa42a46be0
|
||||
project_name: komorebi
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- powershell.exe -Command "New-Item -Path . -Name dummy.go -ItemType file -Force"
|
||||
- powershell.exe -Command "Add-Content -Path .\dummy.go -Value 'package main'"
|
||||
- powershell.exe -Command "Add-Content -Path .\dummy.go -Value 'func main() {}'"
|
||||
|
||||
builds:
|
||||
- id: komorebi
|
||||
main: dummy.go
|
||||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
binary: komorebi
|
||||
hooks:
|
||||
post:
|
||||
- mkdir -p dist/windows_amd64
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebi.exe" ".\dist\komorebi_windows_amd64_v1\komorebi.exe"
|
||||
- id: komorebic
|
||||
main: dummy.go
|
||||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
binary: komorebic
|
||||
hooks:
|
||||
post:
|
||||
- mkdir -p dist/windows_amd64
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic.exe" ".\dist\komorebic_windows_amd64_v1\komorebic.exe"
|
||||
- id: komorebic-no-console
|
||||
main: dummy.go
|
||||
goos: ["windows"]
|
||||
goarch: ["amd64"]
|
||||
binary: komorebic-no-console
|
||||
hooks:
|
||||
post:
|
||||
- mkdir -p dist/windows_amd64
|
||||
- cp ".\target\x86_64-pc-windows-msvc\release\komorebic-no-console.exe" ".\dist\komorebic-no-console_windows_amd64_v1\komorebic-no-console.exe"
|
||||
|
||||
archives:
|
||||
- name_template: "{{ .ProjectName }}-{{ .Version }}-x86_64-pc-windows-msvc"
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
- CHANGELOG.md
|
||||
|
||||
checksum:
|
||||
name_template: checksums.txt
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
45
CONTRIBUTING.md
Normal file
45
CONTRIBUTING.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Contributing to the Project
|
||||
|
||||
The project is a collection of contributions from both the project leaders and
|
||||
community members. There are many ways to contribute, this can include content
|
||||
in the project repositories, as well as contributing in public and private
|
||||
conversation, assisting users, writing blog posts, and many other ways.
|
||||
|
||||
## How contributions are made
|
||||
|
||||
Contributions to the project primarily happen in the project source
|
||||
repositories, but may also occur in other places, such as discussion forums and
|
||||
public and private discourse.
|
||||
|
||||
## Contributing content to the Project
|
||||
|
||||
In order for the project leaders to manage sustained progress toward the
|
||||
project goals and maintain project velocity, focus and quality, the project may
|
||||
adjust the license terms over time.
|
||||
|
||||
Content contributed to the project must therefore be provided under
|
||||
sufficiently liberal terms to allow these operations to proceed unimpeded. As
|
||||
such contributions are accepted with the following understanding:
|
||||
|
||||
* Contributed content is licensed under the terms of the 0-BSD license
|
||||
* Contributors accept the terms of the project license at the time of
|
||||
contribution
|
||||
|
||||
By making a contribution, you accept both the current project license terms,
|
||||
and that all contributions that you have made are provided under the terms of
|
||||
the 0-BSD license.
|
||||
|
||||
## Zero-Clause BSD
|
||||
|
||||
```
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
|
||||
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
```
|
||||
4946
Cargo.lock
generated
4946
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
43
Cargo.toml
43
Cargo.toml
@@ -2,24 +2,46 @@
|
||||
|
||||
resolver = "2"
|
||||
members = [
|
||||
"derive-ahk",
|
||||
"komorebi",
|
||||
"komorebi-client",
|
||||
"komorebi-core",
|
||||
"komorebi-gui",
|
||||
"komorebic",
|
||||
"komorebic-no-console",
|
||||
"komorebi-bar",
|
||||
"komorebi-themes"
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
windows-interface = { version = "0.53" }
|
||||
windows-implement = { version = "0.53" }
|
||||
dunce = "1"
|
||||
dirs = "5"
|
||||
clap = { version = "4", features = ["derive", "wrap_help"] }
|
||||
chrono = "0.4"
|
||||
crossbeam-channel = "0.5"
|
||||
crossbeam-utils = "0.8"
|
||||
color-eyre = "0.6"
|
||||
serde_json = { package = "serde_json_lenient", version = "0.1" }
|
||||
eframe = "0.29"
|
||||
egui_extras = "0.29"
|
||||
dirs = "5"
|
||||
dunce = "1"
|
||||
hotwatch = "0.5"
|
||||
schemars = "0.8"
|
||||
lazy_static = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { package = "serde_json_lenient", version = "0.2" }
|
||||
serde_yaml = "0.9"
|
||||
tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
paste = "1"
|
||||
sysinfo = "0.31"
|
||||
uds_windows = "1"
|
||||
win32-display-data = { git = "https://github.com/LGUG2Z/win32-display-data", rev = "dd65e3f22d0521b78fcddde11abc2a3e9dcc32a8" }
|
||||
windows-implement = { version = "0.58" }
|
||||
windows-interface = { version = "0.58" }
|
||||
windows-core = { version = "0.58" }
|
||||
shadow-rs = "0.35"
|
||||
which = "6"
|
||||
|
||||
[workspace.dependencies.windows]
|
||||
version = "0.54"
|
||||
version = "0.58"
|
||||
features = [
|
||||
"implement",
|
||||
"Win32_System_Com",
|
||||
@@ -36,5 +58,8 @@ features = [
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_Shell_Common",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
"Win32_System_SystemServices"
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Media",
|
||||
"Media_Control"
|
||||
]
|
||||
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Jade Iqbal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
98
LICENSE.md
Normal file
98
LICENSE.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Komorebi License
|
||||
|
||||
Version 1.0.0
|
||||
|
||||
## Acceptance
|
||||
|
||||
In order to get any license under these terms, you must agree
|
||||
to them as both strict obligations and conditions to all
|
||||
your licenses.
|
||||
|
||||
## Copyright License
|
||||
|
||||
The licensor grants you a copyright license for the software
|
||||
to do everything you might do with the software that would
|
||||
otherwise infringe the licensor's copyright in it for any
|
||||
permitted purpose. However, you may only make changes according
|
||||
to the [Changes License](#changes-license), and you may not
|
||||
distribute the software or new works based on the software.
|
||||
|
||||
## Changes License
|
||||
|
||||
The licensor grants you an additional copyright license to
|
||||
make changes for any permitted purpose.
|
||||
|
||||
## Patent License
|
||||
|
||||
The licensor grants you a patent license for the software that
|
||||
covers patent claims the licensor can license, or becomes able
|
||||
to license, that you would infringe by using the software.
|
||||
|
||||
## Personal Uses
|
||||
|
||||
Personal use for research, experiment, and testing for
|
||||
the benefit of public knowledge, personal study, private
|
||||
entertainment, hobby projects, amateur pursuits, or religious
|
||||
observance, without any anticipated commercial application,
|
||||
is use for a permitted purpose.
|
||||
|
||||
## Fair Use
|
||||
|
||||
You may have "fair use" rights for the software under the
|
||||
law. These terms do not limit them.
|
||||
|
||||
## No Other Rights
|
||||
|
||||
These terms do not allow you to sublicense or transfer any of
|
||||
your licenses to anyone else, or prevent the licensor from
|
||||
granting licenses to anyone else. These terms do not imply
|
||||
any other licenses.
|
||||
|
||||
## Patent Defense
|
||||
|
||||
If you make any written claim that the software infringes or
|
||||
contributes to infringement of any patent, your patent license
|
||||
for the software granted under these terms ends immediately. If
|
||||
your company makes such a claim, your patent license ends
|
||||
immediately for work on behalf of your company.
|
||||
|
||||
## Violations
|
||||
|
||||
The first time you are notified in writing that you have
|
||||
violated any of these terms, or done anything with the software
|
||||
not covered by your licenses, your licenses can nonetheless
|
||||
continue if you come into full compliance with these terms,
|
||||
and take practical steps to correct past violations, within
|
||||
32 days of receiving notice. Otherwise, all your licenses
|
||||
end immediately.
|
||||
|
||||
## No Liability
|
||||
|
||||
***As far as the law allows, the software comes as is, without
|
||||
any warranty or condition, and the licensor will not be liable
|
||||
to you for any damages arising out of these terms or the use
|
||||
or nature of the software, under any kind of legal claim.***
|
||||
|
||||
## Definitions
|
||||
|
||||
The **licensor** is the individual or entity offering these
|
||||
terms, and the **software** is the software the licensor makes
|
||||
available under these terms.
|
||||
|
||||
**You** refers to the individual or entity agreeing to these
|
||||
terms.
|
||||
|
||||
**Your company** is any legal entity, sole proprietorship,
|
||||
or other kind of organization that you work for, plus all
|
||||
organizations that have control over, are under the control of,
|
||||
or are under common control with that organization. **Control**
|
||||
means ownership of substantially all the assets of an entity,
|
||||
or the power to direct its management and policies by vote,
|
||||
contract, or otherwise. Control can be direct or indirect.
|
||||
|
||||
**Your licenses** are all the licenses granted to you for the
|
||||
software under these terms.
|
||||
|
||||
**Use** means anything you do with the software requiring one
|
||||
of your licenses.
|
||||
|
||||
8
PRIVACY.md
Normal file
8
PRIVACY.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Privacy Policy for Komorebi
|
||||
|
||||
No data about your device(s) or _komorebi_ usage leave your device.
|
||||
|
||||
## Data Maintained by Komorebi
|
||||
|
||||
_komorebi_ writes log files to and keeps a list of temporary window handles (HWNDs) currently managed by the process in
|
||||
the `$Env:LOCALAPPDATA\komorebi\` directory. This directory is owned by the user running the process.
|
||||
45
README.md
45
README.md
@@ -60,7 +60,7 @@ channel](https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg) where I post
|
||||
_komorebi_ development videos. If you would like to be notified of upcoming
|
||||
videos please subscribe and turn on notifications.
|
||||
|
||||
_komorebi_ is a free and open-source project, and one that encourages you to
|
||||
_komorebi_ is a free and source-available project, and one that encourages you to
|
||||
make charitable donations if you find the software to be useful and have the
|
||||
financial means.
|
||||
|
||||
@@ -82,10 +82,30 @@ A [detailed installation and quickstart
|
||||
guide](https://lgug2z.github.io/komorebi/installation.html) is available which shows how to get started
|
||||
using `scoop`, `winget` or building from source.
|
||||
|
||||
[](https://www.youtube.com/watch?v=H9-_c1egQ4g)
|
||||
[](https://www.youtube.com/watch?v=MMZUAtHbTYY)
|
||||
|
||||
# Comparison With Fancy Zones
|
||||
|
||||
Community member [Olge](https://www.youtube.com/@polle5555) has created an
|
||||
excellent video which compares the default window management features of
|
||||
Windows 11, Fancy Zones and komorebi.
|
||||
|
||||
If you are not familiar with tiling window managers or if you are looking at
|
||||
komorebi and wondering "how is this different from Fancy Zones? 🤔", this short
|
||||
video will answer the majority of your questions.
|
||||
|
||||
[](https://www.youtube.com/watch?v=0LCbS_gm0RA)
|
||||
|
||||
|
||||
# Demonstrations
|
||||
|
||||
[@amnweb](https://github.com/amnweb) showing _komorebi_ `v0.1.28` running on Windows 11 with window borders,
|
||||
unfocused window transparency and animations enabled, using a custom status bar integrated using
|
||||
_komorebi_'s [Window Manager Event Subscriptions](https://github.com/LGUG2Z/komorebi?tab=readme-ov-file#window-manager-event-subscriptions).
|
||||
|
||||
https://github.com/LGUG2Z/komorebi/assets/13164844/21be8dc4-fa76-4f70-9b37-1d316f4b40c2
|
||||
|
||||
|
||||
[@haxibami](https://github.com/haxibami) showing _komorebi_ running on Windows
|
||||
11 with a terminal emulator, a web browser and a code editor. The original
|
||||
video can be viewed
|
||||
@@ -169,6 +189,21 @@ ability for users to specify colours in `komorebi.json` in Hex format alongside
|
||||
There is also a process in place for graceful, non-breaking, deprecation of configuration options that are no longer
|
||||
required.
|
||||
|
||||
## License
|
||||
|
||||
`komorebi` is licensed under the [Komorebi 1.0.0 license](./LICENSE.md), which
|
||||
is a fork of the [PolyForm Strict 1.0.0
|
||||
license](https://polyformproject.org/licenses/strict/1.0.0). On a high level
|
||||
this means that you are free to do whatever you want with `komorebi` other than
|
||||
redistribution, or distribution of new works (ie. hard-forks) based on the
|
||||
software.
|
||||
|
||||
Anyone is free to make their own fork of `komorebi` with changes intended
|
||||
either for personal use or for integration back upstream via pull requests.
|
||||
|
||||
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about how
|
||||
code contributions to `komorebi` are licensed.
|
||||
|
||||
# Development
|
||||
|
||||
If you use IntelliJ, you should enable the following settings to ensure that code generated by macros is recognised by
|
||||
@@ -245,7 +280,7 @@ If the named pipe exists, `komorebi` will start pushing JSON data of successfull
|
||||
|
||||
You may then filter on the `type` key to listen to the events that you are interested in. For a full list of possible
|
||||
notification types, refer to the enum variants of `WindowManagerEvent` in `komorebi` and `SocketMessage`
|
||||
in `komorebi-core`.
|
||||
in `komorebi::core`.
|
||||
|
||||
Below is an example of how you can subscribe to and filter on events using a named pipe in `nodejs`.
|
||||
|
||||
@@ -324,7 +359,7 @@ every `WindowManagerEvent` and `SocketMessage` handled by `komorebi` in a Rust c
|
||||
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"}
|
||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.29"}
|
||||
|
||||
use anyhow::Result;
|
||||
use komorebi_client::Notification;
|
||||
@@ -380,7 +415,7 @@ A TCP listener can optionally be exposed on a port of your choosing with the `--
|
||||
provided to `komorebi` or `komorebic start`, no TCP listener will be created.
|
||||
|
||||
Once created, your client may send
|
||||
any [SocketMessage](https://github.com/LGUG2Z/komorebi/blob/master/komorebi-core/src/lib.rs#L37) to `komorebi` in the
|
||||
any [SocketMessage](https://github.com/LGUG2Z/komorebi/blob/master/komorebi/src/core/mod.rs#L37) to `komorebi` in the
|
||||
same way that `komorebic` would.
|
||||
|
||||
This can be used if you would like to create your own alternative to `komorebic` which incorporates scripting and
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "derive-ahk"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
||||
@@ -1,225 +0,0 @@
|
||||
#![warn(clippy::all, clippy::nursery, clippy::pedantic)]
|
||||
#![allow(clippy::missing_errors_doc)]
|
||||
#![no_implicit_prelude]
|
||||
|
||||
use ::std::clone::Clone;
|
||||
use ::std::convert::From;
|
||||
use ::std::convert::Into;
|
||||
use ::std::format;
|
||||
use ::std::iter::Extend;
|
||||
use ::std::iter::Iterator;
|
||||
use ::std::matches;
|
||||
use ::std::option::Option::Some;
|
||||
use ::std::string::String;
|
||||
use ::std::string::ToString;
|
||||
use ::std::unreachable;
|
||||
use ::std::vec::Vec;
|
||||
|
||||
use ::quote::quote;
|
||||
use ::syn::parse_macro_input;
|
||||
use ::syn::Data;
|
||||
use ::syn::DataEnum;
|
||||
use ::syn::DeriveInput;
|
||||
use ::syn::Fields;
|
||||
use ::syn::FieldsNamed;
|
||||
use ::syn::FieldsUnnamed;
|
||||
use ::syn::Meta;
|
||||
use ::syn::NestedMeta;
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
#[proc_macro_derive(AhkFunction)]
|
||||
pub fn ahk_function(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = input.ident;
|
||||
|
||||
match input.data {
|
||||
Data::Struct(s) => match s.fields {
|
||||
Fields::Named(FieldsNamed { named, .. }) => {
|
||||
let argument_idents = named
|
||||
.iter()
|
||||
// Filter out the flags
|
||||
.filter(|&f| {
|
||||
let mut include = true;
|
||||
for attribute in &f.attrs {
|
||||
if let ::std::result::Result::Ok(Meta::List(list)) =
|
||||
attribute.parse_meta()
|
||||
{
|
||||
for nested in list.nested {
|
||||
if let NestedMeta::Meta(Meta::Path(path)) = nested {
|
||||
if path.is_ident("long") {
|
||||
include = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include
|
||||
})
|
||||
.map(|f| &f.ident);
|
||||
|
||||
let argument_idents_clone = argument_idents.clone();
|
||||
|
||||
let called_arguments = quote! {#(%#argument_idents_clone%) *}
|
||||
.to_string()
|
||||
.replace(" %", "%")
|
||||
.replace("% ", "%")
|
||||
.replace("%%", "% %");
|
||||
|
||||
let flag_idents = named
|
||||
.iter()
|
||||
// Filter only the flags
|
||||
.filter(|f| {
|
||||
let mut include = false;
|
||||
|
||||
for attribute in &f.attrs {
|
||||
if let ::std::result::Result::Ok(Meta::List(list)) =
|
||||
attribute.parse_meta()
|
||||
{
|
||||
for nested in list.nested {
|
||||
if let NestedMeta::Meta(Meta::Path(path)) = nested {
|
||||
// Identify them using the --long flag name
|
||||
if path.is_ident("long") {
|
||||
include = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
include
|
||||
})
|
||||
.map(|f| &f.ident);
|
||||
|
||||
let has_flags = flag_idents.clone().count() != 0;
|
||||
|
||||
if has_flags {
|
||||
let flag_idents_concat = flag_idents.clone();
|
||||
let argument_idents_concat = argument_idents.clone();
|
||||
|
||||
// Concat the args and flag args if there are flags
|
||||
let all_arguments =
|
||||
quote! {#(#argument_idents_concat,) * #(#flag_idents_concat), *}
|
||||
.to_string();
|
||||
|
||||
let flag_idents_clone = flag_idents.clone();
|
||||
let flags = quote! {#(--#flag_idents_clone) *}
|
||||
.to_string()
|
||||
.replace("- - ", "--")
|
||||
.replace('_', "-");
|
||||
|
||||
let called_flag_arguments = quote! {#(%#flag_idents%) *}
|
||||
.to_string()
|
||||
.replace(" %", "%")
|
||||
.replace("% ", "%")
|
||||
.replace("%%", "% %");
|
||||
|
||||
let flags_split: Vec<_> = flags.split(' ').collect();
|
||||
let flag_args_split: Vec<_> = called_flag_arguments.split(' ').collect();
|
||||
let mut consolidated_flags: Vec<String> = Vec::new();
|
||||
|
||||
for (idx, flag) in flags_split.iter().enumerate() {
|
||||
consolidated_flags.push(format!("{} {}", flag, flag_args_split[idx]));
|
||||
}
|
||||
|
||||
let all_flags = consolidated_flags.join(" ");
|
||||
|
||||
quote! {
|
||||
impl AhkFunction for #name {
|
||||
fn generate_ahk_function() -> String {
|
||||
::std::format!(r#"
|
||||
{}({}) {{
|
||||
RunWait, komorebic.exe {} {} {}, , Hide
|
||||
}}"#,
|
||||
::std::stringify!(#name),
|
||||
#all_arguments,
|
||||
::std::stringify!(#name).to_kebab_case(),
|
||||
#called_arguments,
|
||||
#all_flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let arguments = quote! {#(#argument_idents), *}.to_string();
|
||||
|
||||
quote! {
|
||||
impl AhkFunction for #name {
|
||||
fn generate_ahk_function() -> String {
|
||||
::std::format!(r#"
|
||||
{}({}) {{
|
||||
RunWait, komorebic.exe {} {}, , Hide
|
||||
}}"#,
|
||||
::std::stringify!(#name),
|
||||
#arguments,
|
||||
::std::stringify!(#name).to_kebab_case(),
|
||||
#called_arguments
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!("only to be used on structs with named fields"),
|
||||
},
|
||||
_ => unreachable!("only to be used on structs"),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(AhkLibrary)]
|
||||
pub fn ahk_library(input: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let name = input.ident;
|
||||
|
||||
match input.data {
|
||||
Data::Enum(DataEnum { variants, .. }) => {
|
||||
let enums = variants.iter().filter(|&v| {
|
||||
matches!(v.fields, Fields::Unit) || matches!(v.fields, Fields::Unnamed(..))
|
||||
});
|
||||
|
||||
let mut stream = ::proc_macro2::TokenStream::new();
|
||||
|
||||
for variant in enums.clone() {
|
||||
match &variant.fields {
|
||||
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
|
||||
for field in unnamed {
|
||||
stream.extend(quote! {
|
||||
v.push(#field::generate_ahk_function());
|
||||
});
|
||||
}
|
||||
}
|
||||
Fields::Unit => {
|
||||
let name = &variant.ident;
|
||||
stream.extend(quote! {
|
||||
v.push(::std::format!(r#"
|
||||
{}() {{
|
||||
RunWait, komorebic.exe {}, , Hide
|
||||
}}"#,
|
||||
::std::stringify!(#name),
|
||||
::std::stringify!(#name).to_kebab_case()
|
||||
));
|
||||
});
|
||||
}
|
||||
Fields::Named(_) => {
|
||||
unreachable!("only to be used with unnamed and unit fields");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quote! {
|
||||
impl #name {
|
||||
fn generate_ahk_library() -> String {
|
||||
let mut v: Vec<String> = vec![String::from("; Generated by komorebic.exe")];
|
||||
|
||||
#stream
|
||||
|
||||
v.join("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!("only to be used on enums"),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
# active-window-border-colour
|
||||
|
||||
```
|
||||
Set the colour for the active window border
|
||||
|
||||
Usage: komorebic.exe active-window-border-colour [OPTIONS] <R> <G> <B>
|
||||
|
||||
Arguments:
|
||||
<R>
|
||||
Red
|
||||
|
||||
<G>
|
||||
Green
|
||||
|
||||
<B>
|
||||
Blue
|
||||
|
||||
Options:
|
||||
-w, --window-kind <WINDOW_KIND>
|
||||
[default: single]
|
||||
[possible values: single, stack, monocle]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
# active-window-border-offset
|
||||
|
||||
```
|
||||
Set the offset for the active window border
|
||||
|
||||
Usage: komorebic.exe active-window-border-offset <OFFSET>
|
||||
|
||||
Arguments:
|
||||
<OFFSET>
|
||||
Desired offset of the active window border
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,16 +0,0 @@
|
||||
# active-window-border-width
|
||||
|
||||
```
|
||||
Set the width for the active window border
|
||||
|
||||
Usage: komorebic.exe active-window-border-width <WIDTH>
|
||||
|
||||
Arguments:
|
||||
<WIDTH>
|
||||
Desired width of the active window border
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# ahk-library
|
||||
|
||||
```
|
||||
Generate a library of AutoHotKey helper functions
|
||||
|
||||
Usage: komorebic.exe ahk-library
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/animation-duration.md
Normal file
16
docs/cli/animation-duration.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# animation-duration
|
||||
|
||||
```
|
||||
Set the duration for movement animations in ms
|
||||
|
||||
Usage: komorebic.exe animation-duration <DURATION>
|
||||
|
||||
Arguments:
|
||||
<DURATION>
|
||||
Desired animation durations in ms
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/animation-fps.md
Normal file
16
docs/cli/animation-fps.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# animation-fps
|
||||
|
||||
```
|
||||
Set the frames per second for movement animations
|
||||
|
||||
Usage: komorebic.exe animation-fps <FPS>
|
||||
|
||||
Arguments:
|
||||
<FPS>
|
||||
Desired animation frames per second
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
20
docs/cli/animation-style.md
Normal file
20
docs/cli/animation-style.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# animation-style
|
||||
|
||||
```
|
||||
Set the ease function for movement animations
|
||||
|
||||
Usage: komorebic.exe animation-style [OPTIONS]
|
||||
|
||||
Options:
|
||||
-s, --style <STYLE>
|
||||
Desired ease function for animation
|
||||
|
||||
[default: linear]
|
||||
[possible values: linear, ease-in-sine, ease-out-sine, ease-in-out-sine, ease-in-quad, ease-out-quad, ease-in-out-quad, ease-in-cubic, ease-in-out-cubic, ease-in-quart,
|
||||
ease-out-quart, ease-in-out-quart, ease-in-quint, ease-out-quint, ease-in-out-quint, ease-in-expo, ease-out-expo, ease-in-out-expo, ease-in-circ, ease-out-circ, ease-in-out-circ,
|
||||
ease-in-back, ease-out-back, ease-in-out-back, ease-in-elastic, ease-out-elastic, ease-in-out-elastic, ease-in-bounce, ease-out-bounce, ease-in-out-bounce]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,9 +1,9 @@
|
||||
# active-window-border
|
||||
# animation
|
||||
|
||||
```
|
||||
Enable or disable the active window border
|
||||
Enable or disable movement animations
|
||||
|
||||
Usage: komorebic.exe active-window-border <BOOLEAN_STATE>
|
||||
Usage: komorebic.exe animation <BOOLEAN_STATE>
|
||||
|
||||
Arguments:
|
||||
<BOOLEAN_STATE>
|
||||
12
docs/cli/bar-configuration.md
Normal file
12
docs/cli/bar-configuration.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# bar-configuration
|
||||
|
||||
```
|
||||
Show the path to komorebi.bar.json
|
||||
|
||||
Usage: komorebic.exe bar-configuration
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
26
docs/cli/border-colour.md
Normal file
26
docs/cli/border-colour.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# border-colour
|
||||
|
||||
```
|
||||
Set the colour for a window border kind
|
||||
|
||||
Usage: komorebic.exe border-colour [OPTIONS] <R> <G> <B>
|
||||
|
||||
Arguments:
|
||||
<R>
|
||||
Red
|
||||
|
||||
<G>
|
||||
Green
|
||||
|
||||
<B>
|
||||
Blue
|
||||
|
||||
Options:
|
||||
-w, --window-kind <WINDOW_KIND>
|
||||
[default: single]
|
||||
[possible values: single, stack, monocle, unfocused]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
20
docs/cli/border-implementation.md
Normal file
20
docs/cli/border-implementation.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# border-implementation
|
||||
|
||||
```
|
||||
Set the border implementation
|
||||
|
||||
Usage: komorebic.exe border-implementation <STYLE>
|
||||
|
||||
Arguments:
|
||||
<STYLE>
|
||||
Desired border implementation
|
||||
|
||||
Possible values:
|
||||
- komorebi: Use the adjustable komorebi border implementation
|
||||
- windows: Use the thin Windows accent border implementation
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
```
|
||||
16
docs/cli/border-offset.md
Normal file
16
docs/cli/border-offset.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# border-offset
|
||||
|
||||
```
|
||||
Set the border offset
|
||||
|
||||
Usage: komorebic.exe border-offset <OFFSET>
|
||||
|
||||
Arguments:
|
||||
<OFFSET>
|
||||
Desired offset of the window border
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
21
docs/cli/border-style.md
Normal file
21
docs/cli/border-style.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# border-style
|
||||
|
||||
```
|
||||
Set the border style
|
||||
|
||||
Usage: komorebic.exe border-style <STYLE>
|
||||
|
||||
Arguments:
|
||||
<STYLE>
|
||||
Desired border style
|
||||
|
||||
Possible values:
|
||||
- system: Use the system border style
|
||||
- rounded: Use the Windows 11-style rounded borders
|
||||
- square: Use the Windows 10-style square borders
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help (see a summary with '-h')
|
||||
|
||||
```
|
||||
16
docs/cli/border-width.md
Normal file
16
docs/cli/border-width.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# border-width
|
||||
|
||||
```
|
||||
Set the border width
|
||||
|
||||
Usage: komorebic.exe border-width <WIDTH>
|
||||
|
||||
Arguments:
|
||||
<WIDTH>
|
||||
Desired width of the window border
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,9 +1,9 @@
|
||||
# alt-focus-hack
|
||||
# border
|
||||
|
||||
```
|
||||
DEPRECATED since v0.1.22
|
||||
Enable or disable borders
|
||||
|
||||
Usage: komorebic.exe alt-focus-hack <BOOLEAN_STATE>
|
||||
Usage: komorebic.exe border <BOOLEAN_STATE>
|
||||
|
||||
Arguments:
|
||||
<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, grid]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
12
docs/cli/clear-all-workspace-rules.md
Normal file
12
docs/cli/clear-all-workspace-rules.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# clear-all-workspace-rules
|
||||
|
||||
```
|
||||
Remove all application association rules for all workspaces
|
||||
|
||||
Usage: komorebic.exe clear-all-workspace-rules
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/clear-named-workspace-rules.md
Normal file
16
docs/cli/clear-named-workspace-rules.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# clear-named-workspace-rules
|
||||
|
||||
```
|
||||
Remove all application association rules for a named workspace
|
||||
|
||||
Usage: komorebic.exe clear-named-workspace-rules <WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<WORKSPACE>
|
||||
Name of a workspace
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
19
docs/cli/clear-workspace-rules.md
Normal file
19
docs/cli/clear-workspace-rules.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# clear-workspace-rules
|
||||
|
||||
```
|
||||
Remove all application association rules for a workspace by monitor and workspace index
|
||||
|
||||
Usage: komorebic.exe clear-workspace-rules <MONITOR> <WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<MONITOR>
|
||||
Monitor index (zero-indexed)
|
||||
|
||||
<WORKSPACE>
|
||||
Workspace index on the specified monitor (zero-indexed)
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
# complete-configuration
|
||||
|
||||
```
|
||||
Signal that the final configuration option has been sent
|
||||
For legacy komorebi.ahk or komorebi.ps1 configurations, signal that the final configuration option has been sent
|
||||
|
||||
Usage: komorebic.exe complete-configuration
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ Arguments:
|
||||
Possible values:
|
||||
- swap: Swap the window container with the window container at the edge of the adjacent monitor
|
||||
- insert: Insert the window container into the focused workspace on the adjacent monitor
|
||||
- no-op: Do nothing if trying to move a window container in the direction of an adjacent monitor
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
16
docs/cli/cycle-move-workspace-to-monitor.md
Normal file
16
docs/cli/cycle-move-workspace-to-monitor.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# cycle-move-workspace-to-monitor
|
||||
|
||||
```
|
||||
Move the focused workspace monitor in the given cycle direction
|
||||
|
||||
Usage: komorebic.exe cycle-move-workspace-to-monitor <CYCLE_DIRECTION>
|
||||
|
||||
Arguments:
|
||||
<CYCLE_DIRECTION>
|
||||
[possible values: previous, next]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -18,6 +18,9 @@ Options:
|
||||
--ahk
|
||||
Enable autostart of ahk
|
||||
|
||||
--bar
|
||||
Enable autostart of komorebi-bar
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe float-rule <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
16
docs/cli/focus-stack-window.md
Normal file
16
docs/cli/focus-stack-window.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# focus-stack-window
|
||||
|
||||
```
|
||||
Focus the specified window index in the focused stack
|
||||
|
||||
Usage: komorebic.exe focus-stack-window <TARGET>
|
||||
|
||||
Arguments:
|
||||
<TARGET>
|
||||
Target index (zero-indexed)
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
12
docs/cli/global-state.md
Normal file
12
docs/cli/global-state.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# global-state
|
||||
|
||||
```
|
||||
Show a JSON representation of the current global state
|
||||
|
||||
Usage: komorebic.exe global-state
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
12
docs/cli/gui.md
Normal file
12
docs/cli/gui.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# gui
|
||||
|
||||
```
|
||||
Launch the komorebi-gui debugging tool
|
||||
|
||||
Usage: komorebic.exe gui
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,19 +0,0 @@
|
||||
# identify-border-overflow-application
|
||||
|
||||
```
|
||||
Identify an application that has overflowing borders
|
||||
|
||||
Usage: komorebic.exe identify-border-overflow-application <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-layered-application <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-object-name-change-application <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe identify-tray-application <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe initial-named-workspace-rule <IDENTIFIER> <ID> <WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe initial-workspace-rule <IDENTIFIER> <ID> <MONITOR> <WORKSPA
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe manage-rule <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
12
docs/cli/monitor-information.md
Normal file
12
docs/cli/monitor-information.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# monitor-information
|
||||
|
||||
```
|
||||
Show information about connected monitors
|
||||
|
||||
Usage: komorebic.exe monitor-information
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
19
docs/cli/move-to-monitor-workspace.md
Normal file
19
docs/cli/move-to-monitor-workspace.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# move-to-monitor-workspace
|
||||
|
||||
```
|
||||
Move the focused window to the specified monitor workspace
|
||||
|
||||
Usage: komorebic.exe move-to-monitor-workspace <TARGET_MONITOR> <TARGET_WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<TARGET_MONITOR>
|
||||
Target monitor index (zero-indexed)
|
||||
|
||||
<TARGET_WORKSPACE>
|
||||
Workspace index on the target monitor (zero-indexed)
|
||||
|
||||
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, grid]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -10,7 +10,7 @@ Arguments:
|
||||
Target workspace name
|
||||
|
||||
<VALUE>
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe named-workspace-rule <IDENTIFIER> <ID> <WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
16
docs/cli/promote-window.md
Normal file
16
docs/cli/promote-window.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# promote-window
|
||||
|
||||
```
|
||||
Promote the window in the specified direction
|
||||
|
||||
Usage: komorebic.exe promote-window <OPERATION_DIRECTION>
|
||||
|
||||
Arguments:
|
||||
<OPERATION_DIRECTION>
|
||||
[possible values: left, right, up, down]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
# reload-configuration
|
||||
|
||||
```
|
||||
Reload ~/komorebi.ahk (if it exists)
|
||||
Reload legacy komorebi.ahk or komorebi.ps1 configurations (if they exist)
|
||||
|
||||
Usage: komorebic.exe reload-configuration
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe remove-title-bar <IDENTIFIER> <ID>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
16
docs/cli/replace-configuration.md
Normal file
16
docs/cli/replace-configuration.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# replace-configuration
|
||||
|
||||
```
|
||||
Replace the configuration of a running instance of komorebi from a static configuration file
|
||||
|
||||
Usage: komorebic.exe replace-configuration <PATH>
|
||||
|
||||
Arguments:
|
||||
<PATH>
|
||||
Static configuration JSON file from which the configuration should be loaded
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
12
docs/cli/stack-all.md
Normal file
12
docs/cli/stack-all.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# stack-all
|
||||
|
||||
```
|
||||
Stack all windows on the focused workspace
|
||||
|
||||
Usage: komorebic.exe stack-all
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -24,6 +24,9 @@ Options:
|
||||
--ahk
|
||||
Start autohotkey configuration file
|
||||
|
||||
--bar
|
||||
Start komorebi-bar in a background process
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ Options:
|
||||
--whkd
|
||||
Stop whkd if it is running as a background process
|
||||
|
||||
--bar
|
||||
Stop komorebi-bar if it is running as a background process
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
12
docs/cli/toggle-transparency.md
Normal file
12
docs/cli/toggle-transparency.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# toggle-transparency
|
||||
|
||||
```
|
||||
Toggle transparency for unfocused windows
|
||||
|
||||
Usage: komorebic.exe toggle-transparency
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/transparency-alpha.md
Normal file
16
docs/cli/transparency-alpha.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# transparency-alpha
|
||||
|
||||
```
|
||||
Set the alpha value for unfocused window transparency
|
||||
|
||||
Usage: komorebic.exe transparency-alpha <ALPHA>
|
||||
|
||||
Arguments:
|
||||
<ALPHA>
|
||||
Alpha
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/transparency.md
Normal file
16
docs/cli/transparency.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# transparency
|
||||
|
||||
```
|
||||
Enable or disable transparency for unfocused windows
|
||||
|
||||
Usage: komorebic.exe transparency <BOOLEAN_STATE>
|
||||
|
||||
Arguments:
|
||||
<BOOLEAN_STATE>
|
||||
[possible values: enable, disable]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
12
docs/cli/unstack-all.md
Normal file
12
docs/cli/unstack-all.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# unstack-all
|
||||
|
||||
```
|
||||
Unstack all windows in the focused container
|
||||
|
||||
Usage: komorebic.exe unstack-all
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -1,7 +1,7 @@
|
||||
# watch-configuration
|
||||
|
||||
```
|
||||
Enable or disable watching of ~/komorebi.ahk (if it exists)
|
||||
Enable or disable watching of legacy komorebi.ahk or komorebi.ps1 configurations (if they exist)
|
||||
|
||||
Usage: komorebic.exe watch-configuration <BOOLEAN_STATE>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Arguments:
|
||||
Possible values:
|
||||
- hide: Use the SW_HIDE flag to hide windows when switching workspaces (has issues with Electron apps)
|
||||
- minimize: Use the SW_MINIMIZE flag to hide windows when switching workspaces (has issues with frequent workspace switching)
|
||||
- cloak: Use the undocumented SetCloak Win32 function to hide windows when switching workspaces (has foregrounding issues)
|
||||
- cloak: Use the undocumented SetCloak Win32 function to hide windows when switching workspaces
|
||||
|
||||
Options:
|
||||
-h, --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, grid]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
||||
|
||||
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, grid]
|
||||
[possible values: bsp, columns, rows, vertical-stack, horizontal-stack, ultrawide-vertical-stack, grid, right-main-vertical-stack]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
|
||||
@@ -7,7 +7,7 @@ Usage: komorebic.exe workspace-rule <IDENTIFIER> <ID> <MONITOR> <WORKSPACE>
|
||||
|
||||
Arguments:
|
||||
<IDENTIFIER>
|
||||
[possible values: exe, class, title]
|
||||
[possible values: exe, class, title, path]
|
||||
|
||||
<ID>
|
||||
Identifier as a string
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Active Window Border
|
||||
|
||||
If you would like to add a visual border around the currently focused window,
|
||||
ensure the following options are defined in the `komorebi.json` configuration
|
||||
file.
|
||||
|
||||
```json
|
||||
{
|
||||
"active_window_border": true,
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
It is important to note that the active window border will only apply to
|
||||
windows managed by `komorebi`.
|
||||
|
||||
This feature is not considered stable and you may encounter visual artifacts
|
||||
from time to time.
|
||||
|
||||
[](https://www.youtube.com/watch?v=7_9D22t7KK4)
|
||||
25
docs/common-workflows/animations.md
Normal file
25
docs/common-workflows/animations.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Animations
|
||||
|
||||
If you would like to add window movement animations, ensure the following options are
|
||||
defined in the `komorebi.json` configuration file.
|
||||
|
||||
```json
|
||||
{
|
||||
"animation": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Window movement animations only apply to actions taking place within the same monitor
|
||||
workspace.
|
||||
|
||||
You can optionally set a custom duration in ms with `animation.duration` (default: `250`),
|
||||
a custom style with `animation.style` (default: `Linear`), and a custom FPS value with
|
||||
`animation.fps` (default: `60`).
|
||||
|
||||
It is important to note that higher `fps` and a longer `duration` settings will result
|
||||
in increased CPU usage.
|
||||
|
||||
This feature is not considered stable, and you may encounter visual artifacts
|
||||
from time to time.
|
||||
@@ -1,6 +1,4 @@
|
||||
# AutoHotKey
|
||||
|
||||
<!-- TODO: Update this completely -->
|
||||
# AutoHotkey
|
||||
|
||||
If you would like to use Autohotkey, please make sure you have AutoHotKey v2
|
||||
installed.
|
||||
@@ -9,22 +7,16 @@ Generally, users who opt for AHK will have specific needs that can only be
|
||||
addressed by the advanced functionality of AHK, and so they are assumed to be
|
||||
able to craft their own configuration files.
|
||||
|
||||
If you would like to try out AHK, a simple sample configuration powered by
|
||||
`komorebic.lib.ahk` is provided as a starting point. This sample configuration
|
||||
does not take into account the use of a static configuration file; if you
|
||||
choose to use a static configuration file alongside AHK, you can remove all the
|
||||
configuration options from your `komorebi.ahk` and use it solely to handle
|
||||
hotkey bindings.
|
||||
If you would like to try out AHK, here is a simple sample configuration which
|
||||
largely matches the `whkdrc` sample configuration.
|
||||
|
||||
|
||||
```powershell
|
||||
# save the latest generated komorebic library to ~/komorebic.lib.ahk
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.20/komorebic.lib.ahk -OutFile $Env:USERPROFILE\komorebic.lib.ahk
|
||||
|
||||
# save the latest generated app-specific config tweaks and fixes to ~/komorebi.generated.ahk
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.20/komorebi.generated.ahk -OutFile $Env:USERPROFILE\komorebi.generated.ahk
|
||||
|
||||
# save the sample komorebi configuration file to ~/komorebi.ahk
|
||||
iwr https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.20/komorebi.sample.ahk -OutFile $Env:USERPROFILE\komorebi.ahk
|
||||
```autohotkey
|
||||
{% include "./komorebi.ahk.txt" %}
|
||||
```
|
||||
|
||||
By default, the `komorebi.ahk` file should be located in the `$Env:USERPROFILE`
|
||||
directory, however, if `$Env:KOMOREBI_CONFIG_HOME` is set, it should be located
|
||||
there.
|
||||
|
||||
Once the file is in place, you can stop komorebi and whkd by running `komorebic stop --whkd`,
|
||||
and then start komorebi with Autohotkey by running `komorebic start --ahk`.
|
||||
|
||||
28
docs/common-workflows/borders.md
Normal file
28
docs/common-workflows/borders.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Borders
|
||||
|
||||
If you would like to add a visual border around both the currently focused window
|
||||
and unfocused windows ensure the following options are defined in the `komorebi.json`
|
||||
configuration file.
|
||||
|
||||
```json
|
||||
{
|
||||
"border": true,
|
||||
"border_width": 8,
|
||||
"border_offset": -1,
|
||||
"border_style": "System",
|
||||
"border_colours": {
|
||||
"single": "#42a5f5",
|
||||
"stack": "#00a542",
|
||||
"monocle": "#ff3399",
|
||||
"unfocused": "#808080"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It is important to note that borders will only apply to windows managed by `komorebi`.
|
||||
|
||||
This feature is not considered stable, and you may encounter visual artifacts
|
||||
from time to time.
|
||||
|
||||
[](https://www.youtube.com/watch?v=7_9D22t7KK4)
|
||||
@@ -1,4 +1,4 @@
|
||||
# Dynamically Layout Switching
|
||||
# Dynamic Layout Switching
|
||||
|
||||
With `komorebi` it is possible to define rules to automatically change the
|
||||
layout on a specified workspace when a threshold of window containers is met.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
❗️**NOTE**: A significant number of force-manage window rules for the most
|
||||
common applications are [already generated for
|
||||
you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
|
||||
you](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
|
||||
|
||||
In some rare cases, a window may not automatically be registered to be managed
|
||||
by `komorebi`. You can add rules to enforce this behaviour in the
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
❗️**NOTE**: A significant number of ignored window rules for the most common
|
||||
applications are [already generated for
|
||||
you](https://github.com/LGUG2Z/komorebi/#generating-common-application-specific-configurations)
|
||||
you](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
|
||||
|
||||
Sometimes you will want a specific application to never be tiled, and instead
|
||||
float all the time. You can add rules to enforce this behaviour in the
|
||||
|
||||
17
docs/common-workflows/set-display-index.md
Normal file
17
docs/common-workflows/set-display-index.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Setting a Given Display to a Specific Index
|
||||
|
||||
If you would like `komorebi` to remember monitor index positions, you will need to set the `display_index_preferences`
|
||||
configuration option in the static configuration file.
|
||||
|
||||
Display IDs can be found using `komorebic monitor-information`.
|
||||
|
||||
Then, in `komorebi.json`, you simply need to specify the preferred index position for each display ID:
|
||||
|
||||
```json
|
||||
{
|
||||
"display_index_preferences": {
|
||||
"0": "DEL4310-5&1a6c0954&0&UID209155",
|
||||
"1": "<another-display_id>"
|
||||
}
|
||||
}
|
||||
```
|
||||
23
docs/common-workflows/stackbar.md
Normal file
23
docs/common-workflows/stackbar.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Stackbar
|
||||
|
||||
If you would like to add a visual stackbar to show which windows are in a container
|
||||
stack ensure the following options are defined in the `komorebi.json` configuration
|
||||
file.
|
||||
|
||||
```json
|
||||
{
|
||||
"stackbar": {
|
||||
"height": 40,
|
||||
"mode": "OnStack",
|
||||
"tabs": {
|
||||
"width": 300,
|
||||
"focused_text": "#00a542",
|
||||
"unfocused_text": "#b3b3b3",
|
||||
"background": "#141414"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This feature is not considered stable, and you may encounter visual artifacts
|
||||
from time to time.
|
||||
@@ -16,16 +16,17 @@ 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.
|
||||
With the example configurations downloaded, you can now start `komorebi`,
|
||||
`komorebi-bar` and `whkd`.
|
||||
|
||||
```powershell
|
||||
komorebic start --whkd
|
||||
komorebic start --whkd --bar
|
||||
```
|
||||
|
||||
## komorebi.json
|
||||
|
||||
The example window manager configuration sets some sane defaults and provides
|
||||
five preconfigured workspaces on the primary monitor each with a different
|
||||
seven preconfigured workspaces on the primary monitor each with a different
|
||||
layout.
|
||||
|
||||
```json
|
||||
@@ -101,6 +102,16 @@ monocle.
|
||||
+-------+-----+
|
||||
```
|
||||
|
||||
#### RightMainVerticalStack
|
||||
|
||||
```
|
||||
+-----+-------+
|
||||
| | |
|
||||
+-----+ |
|
||||
| | |
|
||||
+-----+-------+
|
||||
```
|
||||
|
||||
#### Horizontal Stack
|
||||
|
||||
```
|
||||
@@ -122,6 +133,7 @@ monocle.
|
||||
```
|
||||
|
||||
#### Rows
|
||||
|
||||
If you have a vertical monitor, I recommend using this layout.
|
||||
|
||||
```
|
||||
@@ -133,6 +145,7 @@ If you have a vertical monitor, I recommend using this layout.
|
||||
```
|
||||
|
||||
#### Ultrawide Vertical Stack
|
||||
|
||||
If you have an ultrawide monitor, I recommend using this layout.
|
||||
|
||||
```
|
||||
@@ -146,6 +159,7 @@ 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!
|
||||
|
||||
```
|
||||
@@ -162,8 +176,8 @@ If you like the `grid` layout in [LeftWM](https://github.com/leftwm/leftwm-layou
|
||||
## whkdrc
|
||||
|
||||
`whkd` is a fairly basic piece of software with a simple configuration format:
|
||||
key bindings go to the left of the, and shell commands go to the right of the
|
||||
colon.
|
||||
key bindings go to the left of the colon, and shell commands go to the right of the
|
||||
colon. By default, the `whkdrc` file should be located in the `$Env:USERPROFILE/.config/` directory.
|
||||
|
||||
Please remember that `whkd` does not support overriding Microsoft's limitations
|
||||
on hotkey bindings that include the `Windows` key. If this is important to you,
|
||||
@@ -183,7 +197,8 @@ which shell you use in your terminal.
|
||||
* `powershell` - set this if you are using the version of PowerShell that comes
|
||||
installed with Windows 10+ (the executable file for this is `powershell.exe`)
|
||||
|
||||
* `pwsh` - set this if you are using PowerShell 7+, which you have installed yourself either through the Windows Store or WinGet (the executable file for this is `pwsh.exe`)
|
||||
* `pwsh` - set this if you are using PowerShell 7+, which you have installed yourself either through the Windows Store
|
||||
or WinGet (the executable file for this is `pwsh.exe`)
|
||||
|
||||
* `cmd` - set this if you don't want to use PowerShell at all and instead you
|
||||
want to call commands through the shell used by the old-school Command
|
||||
@@ -199,3 +214,24 @@ reference.
|
||||
If you want to use one of those key codes, put them into lower case and remove
|
||||
the `VK_` prefix. For example, the keycode `VK_OEM_PLUS` becomes `oem_plus` in
|
||||
the sample configuration above.
|
||||
|
||||
## komorebi.bar.json
|
||||
|
||||
The example status bar configuration sets some sane defaults and provides
|
||||
a number of pre-configured widgets on the primary monitor.
|
||||
|
||||
```json
|
||||
{% include "./komorebi.bar.example.json" %}
|
||||
```
|
||||
|
||||
### Themes
|
||||
|
||||
Themes can be set in either `komorebi.json` or `komorebi.bar.json`. If set
|
||||
in `komorebi.json`, the theme will be applied to both komorebi's borders and
|
||||
stackbars as well as the status bar.
|
||||
|
||||
If set in `komorebi.bar.json`, the theme will only be applied to the status bar.
|
||||
|
||||
All [Catppuccin palette variants](https://catppuccin.com/)
|
||||
and [most Base16 palette variants](https://tinted-theming.github.io/base16-gallery/)
|
||||
are available as themes.
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Getting started
|
||||
|
||||
`komorebi` is a tiling window manager for Windows that is comprised comprised
|
||||
of two main binaries, `komorebi.exe`, which contains the window manager itself,
|
||||
`komorebi` is a tiling window manager for Windows that is comprised of two
|
||||
main binaries, `komorebi.exe`, which contains the window manager itself,
|
||||
and `komorebic.exe`, which is the main way to send commands to the tiling
|
||||
window manager.
|
||||
|
||||
It is important to note that neither `komorebi.exe` or `komorebic.exe` handle
|
||||
It is important to note that neither `komorebi.exe` nor `komorebic.exe` handle
|
||||
key bindings, because `komorebi` is a tiling window manager and not a hotkey
|
||||
daemon.
|
||||
|
||||
@@ -23,20 +23,25 @@ suggest that once you are familiar with the main `komorebic.exe` commands used
|
||||
to manipulate the window manager, you use
|
||||
[AutoHotKey](https://www.autohotkey.com/) to handle your key bindings.
|
||||
|
||||
`komorebi` also includes `komorebi-bar.exe`, a simple and reliable status bar which
|
||||
is deeply integrated with the tiling window manager, and can be customized with
|
||||
various widgets and themes.
|
||||
|
||||
## Installation
|
||||
|
||||
`komorebi` is available pre-built to install via
|
||||
[Scoop](https://scoop.sh/#/apps?q=komorebi) and
|
||||
[WinGet](https://winget.run/pkg/LGUG2Z/komorebi), and you may also built
|
||||
[WinGet](https://winget.run/pkg/LGUG2Z/komorebi), and you may also build
|
||||
it from [source](https://github.com/LGUG2Z/komorebi) if you would prefer.
|
||||
|
||||
- [Scoop](#scoop)
|
||||
- [WinGet](#winget)
|
||||
- [Building from source](#building-from-source)
|
||||
- [Scoop](#scoop)
|
||||
- [WinGet](#winget)
|
||||
- [Building from source](#building-from-source)
|
||||
- [Offline](#offline)
|
||||
|
||||
## Long path support
|
||||
|
||||
It highly recommended that you enable support for long paths in Windows by
|
||||
It is highly recommended that you enable support for long paths in Windows by
|
||||
running the following command in an Administrator Terminal before installing
|
||||
`komorebi`.
|
||||
|
||||
@@ -44,6 +49,12 @@ running the following command in an Administrator Terminal before installing
|
||||
Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name 'LongPathsEnabled' -Value 1
|
||||
```
|
||||
|
||||
## Disabling unnecessary system animations
|
||||
|
||||
It is highly recommended that you enable the "Turn off all unnecessary animations (when possible)" option in
|
||||
"Control Panel > Ease of Access > Ease of Access Centre / Make the computer easier to see" for the best performance with
|
||||
komorebi.
|
||||
|
||||
## Scoop
|
||||
|
||||
Make sure you have installed [`scoop`](https://scoop.sh) and verified that
|
||||
@@ -107,7 +118,36 @@ Clone the git repository, enter the directory, and build the following binaries:
|
||||
cargo +stable install --path komorebi --locked
|
||||
cargo +stable install --path komorebic --locked
|
||||
cargo +stable install --path komorebic-no-console --locked
|
||||
cargo +stable install --path komorebi-gui --locked
|
||||
cargo +stable install --path komorebi-bar --locked
|
||||
```
|
||||
|
||||
If the binaries have been built and added to your `$PATH` correctly, you should
|
||||
see some output when running `komorebi --help` and `komorebic --help`
|
||||
|
||||
### Offline
|
||||
|
||||
Download the latest [komorebi](https://github.com/LGUG2Z/komorebi/releases)
|
||||
and [whkd](https://github.com/LGUG2Z/whkd/releases) MSI installers on an internet-connected computer, then copy them to
|
||||
an offline machine to install.
|
||||
|
||||
Once installed, proceed to get the [example configurations](example-configurations.md) (none of the commands for
|
||||
first-time set up and running komorebi require an internet connection).
|
||||
|
||||
## Uninstallation
|
||||
|
||||
Before uninstalling, first run `komorebic stop --whkd --bar` to make sure that
|
||||
the `komorebi`, `komorebi-bar` and `whkd` processes have been stopped.
|
||||
|
||||
Then, depending on whether you installed with Scoop or WinGet, run `scoop
|
||||
uninstall komorebi whkd` or `winget uninstall LGUG2Z.komorebi LGUG2Z.whkd`.
|
||||
|
||||
Finally, you can run the following commands in a PowerShell prompt to clean up
|
||||
files created by the `quickstart` command and any other runtime files:
|
||||
|
||||
```powershell
|
||||
rm $Env:USERPROFILE\komorebi.json
|
||||
rm $Env:USERPROFILE\applications.yaml
|
||||
rm $Env:USERPROFILE\.config\whkdrc
|
||||
rm -r -Force $Env:LOCALAPPDATA\komorebi
|
||||
```
|
||||
|
||||
71
docs/komorebi.ahk.txt
Normal file
71
docs/komorebi.ahk.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
#Requires AutoHotkey v2.0.2
|
||||
#SingleInstance Force
|
||||
|
||||
Komorebic(cmd) {
|
||||
RunWait(format("komorebic.exe {}", cmd), , "Hide")
|
||||
}
|
||||
|
||||
!q::Komorebic("close")
|
||||
!m::Komorebic("minimize")
|
||||
|
||||
; Focus windows
|
||||
!h::Komorebic("focus left")
|
||||
!j::Komorebic("focus down")
|
||||
!k::Komorebic("focus up")
|
||||
!l::Komorebic("focus right")
|
||||
|
||||
!+[::Komorebic("cycle-focus previous")
|
||||
!+]::Komorebic("cycle-focus next")
|
||||
|
||||
; Move windows
|
||||
!+h::Komorebic("move left")
|
||||
!+j::Komorebic("move down")
|
||||
!+k::Komorebic("move up")
|
||||
!+l::Komorebic("move right")
|
||||
|
||||
; Stack windows
|
||||
!Left::Komorebic("stack left")
|
||||
!Down::Komorebic("stack down")
|
||||
!Up::Komorebic("stack up")
|
||||
!Right::Komorebic("stack right")
|
||||
!;::Komorebic("unstack")
|
||||
![::Komorebic("cycle-stack previous")
|
||||
!]::Komorebic("cycle-stack next")
|
||||
|
||||
; Resize
|
||||
!=::Komorebic("resize-axis horizontal increase")
|
||||
!-::Komorebic("resize-axis horizontal decrease")
|
||||
!+=::Komorebic("resize-axis vertical increase")
|
||||
!+_::Komorebic("resize-axis vertical decrease")
|
||||
|
||||
; Manipulate windows
|
||||
!t::Komorebic("toggle-float")
|
||||
!f::Komorebic("toggle-monocle")
|
||||
|
||||
; Window manager options
|
||||
!+r::Komorebic("retile")
|
||||
!p::Komorebic("toggle-pause")
|
||||
|
||||
; Layouts
|
||||
!x::Komorebic("flip-layout horizontal")
|
||||
!y::Komorebic("flip-layout vertical")
|
||||
|
||||
; Workspaces
|
||||
!1::Komorebic("focus-workspace 0")
|
||||
!2::Komorebic("focus-workspace 1")
|
||||
!3::Komorebic("focus-workspace 2")
|
||||
!4::Komorebic("focus-workspace 3")
|
||||
!5::Komorebic("focus-workspace 4")
|
||||
!6::Komorebic("focus-workspace 5")
|
||||
!7::Komorebic("focus-workspace 6")
|
||||
!8::Komorebic("focus-workspace 7")
|
||||
|
||||
; Move windows across workspaces
|
||||
!+1::Komorebic("move-to-workspace 0")
|
||||
!+2::Komorebic("move-to-workspace 1")
|
||||
!+3::Komorebic("move-to-workspace 2")
|
||||
!+4::Komorebic("move-to-workspace 3")
|
||||
!+5::Komorebic("move-to-workspace 4")
|
||||
!+6::Komorebic("move-to-workspace 5")
|
||||
!+7::Komorebic("move-to-workspace 6")
|
||||
!+8::Komorebic("move-to-workspace 7")
|
||||
76
docs/komorebi.bar.example.json
Normal file
76
docs/komorebi.bar.example.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.29/schema.bar.json",
|
||||
"monitor": {
|
||||
"index": 0,
|
||||
"work_area_offset": {
|
||||
"left": 0,
|
||||
"top": 40,
|
||||
"right": 0,
|
||||
"bottom": 40
|
||||
}
|
||||
},
|
||||
"font_family": "JetBrains Mono",
|
||||
"theme": {
|
||||
"palette": "Base16",
|
||||
"name": "Ashes",
|
||||
"accent": "Base0D"
|
||||
},
|
||||
"left_widgets": [
|
||||
{
|
||||
"Komorebi": {
|
||||
"workspaces": {
|
||||
"enable": true,
|
||||
"hide_empty_workspaces": false
|
||||
},
|
||||
"layout": {
|
||||
"enable": true
|
||||
},
|
||||
"focused_window": {
|
||||
"enable": true,
|
||||
"show_icon": true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"right_widgets": [
|
||||
{
|
||||
"Media": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Storage": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Memory": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Network": {
|
||||
"enable": true,
|
||||
"show_total_data_transmitted": true,
|
||||
"show_network_activity": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Date": {
|
||||
"enable": true,
|
||||
"format": "DayDateMonthYear"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Time": {
|
||||
"enable": true,
|
||||
"format": "TwentyFourHour"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Battery": {
|
||||
"enable": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,26 +1,57 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.22/schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.29/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": true,
|
||||
"border_width": 8,
|
||||
"border_offset": -1,
|
||||
"active_window_border": false,
|
||||
"active_window_border_colours": {
|
||||
"single": "#42a5f5",
|
||||
"stack": "#00a542",
|
||||
"monocle": "#ff3399"
|
||||
"theme": {
|
||||
"palette": "Base16",
|
||||
"name": "Ashes",
|
||||
"unfocused_border": "Base03",
|
||||
"bar_accent": "Base0D"
|
||||
},
|
||||
"stackbar": {
|
||||
"height": 40,
|
||||
"mode": "OnStack",
|
||||
"tabs": {
|
||||
"width": 300
|
||||
}
|
||||
},
|
||||
"monitors": [
|
||||
{
|
||||
"workspaces": [
|
||||
{ "name": "I", "layout": "BSP" },
|
||||
{ "name": "II", "layout": "VerticalStack" },
|
||||
{ "name": "III", "layout": "HorizontalStack" },
|
||||
{ "name": "IV", "layout": "UltrawideVerticalStack" },
|
||||
{ "name": "V", "layout": "Rows" }
|
||||
{
|
||||
"name": "I",
|
||||
"layout": "BSP"
|
||||
},
|
||||
{
|
||||
"name": "II",
|
||||
"layout": "VerticalStack"
|
||||
},
|
||||
{
|
||||
"name": "III",
|
||||
"layout": "HorizontalStack"
|
||||
},
|
||||
{
|
||||
"name": "IV",
|
||||
"layout": "UltrawideVerticalStack"
|
||||
},
|
||||
{
|
||||
"name": "V",
|
||||
"layout": "Rows"
|
||||
},
|
||||
{
|
||||
"name": "VI",
|
||||
"layout": "Grid"
|
||||
},
|
||||
{
|
||||
"name": "VII",
|
||||
"layout": "RightMainVerticalStack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# v0.1.22
|
||||
|
||||
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.
|
||||
|
||||
@@ -49,8 +51,8 @@ A 1px border is drawn around the window edge. Users may see a gap for a single p
|
||||
transparent edge - this is the windows themed edge, and is not present for all applications.
|
||||
|
||||
```json
|
||||
{
|
||||
{
|
||||
"border_offset": 0,
|
||||
"border_width": 1
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
124
docs/troubleshooting.md
Normal file
124
docs/troubleshooting.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Troubleshooting
|
||||
|
||||
## AutoHotKey executable not found
|
||||
|
||||
If you try to start komorebi with AHK using `komorebic start --ahk`, and you have
|
||||
not installed AHK using `scoop`, you'll probably receive an error:
|
||||
|
||||
```text
|
||||
Error: could not find autohotkey, please make sure it is installed before using the --ahk flag
|
||||
```
|
||||
|
||||
Depending on how AHK is installed the executable on your system may have a
|
||||
different name. In order to account for this, you may set the `KOMOREBI_AHK_EXE`
|
||||
environment variable in your
|
||||
[PowerShell profile](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7.4)
|
||||
to match the name of the executable as it is found on your system.
|
||||
|
||||
After setting `KOMOREBI_AHK_EXE` make sure to either reload your PowerShell
|
||||
profile or open a new terminal tab.
|
||||
|
||||
## Komorebi is unresponsive when the display wakes from sleep
|
||||
|
||||
This can happen in rare cases when your monitor state is not preserved after it
|
||||
wakes from sleep.
|
||||
|
||||
### Problem
|
||||
|
||||
Your hotkeys in _whkd_ work, but it feels as if _komorebi_ knows nothing about
|
||||
the previous state (you can't control previous windows, although newly launched ones
|
||||
can be manipulated as normal).
|
||||
|
||||
### Solution
|
||||
|
||||
Some monitors, such as the Samsung G8/G9 (LED, Neo, OLED) have an _adaptive
|
||||
sync_ or _variable refresh rate_ setting within the actual monitor OSD that can
|
||||
disrupt how the device is persisted in the _komorebi_ state following suspension.
|
||||
|
||||
To fix this, please try to disable _Adaptive Sync_ or any other _VRR_ branded
|
||||
alias by referring to the manufacturer's documentation.
|
||||
|
||||
!!! warning
|
||||
|
||||
Disabling VRR within Windows (e.g. _Nvidia Control Panel_) may work and can indeed
|
||||
change the configuration you see within your monitor's OSD, but some monitors
|
||||
will re-enable the setting regardless following suspension.
|
||||
|
||||
### Reproducing
|
||||
|
||||
Ensure _komorebi_ is in an operational state by executing `komorebic start` as
|
||||
normal.
|
||||
|
||||
If _komorebi_ is already unresponsive, then please restart _komorebi_ first by
|
||||
running `komorebic stop` and `komorebic start`.
|
||||
|
||||
1. **`komorebic state`**
|
||||
|
||||
```json
|
||||
{
|
||||
"monitors": {
|
||||
"elements": [
|
||||
{
|
||||
"id": 65537,
|
||||
"name": "DISPLAY1",
|
||||
"device": "SAM71AA",
|
||||
"device_id": "SAM71AA-5&a1a3e88&0&UID24834",
|
||||
"size": {
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"right": 5120,
|
||||
"bottom": 1440
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This appears to be fine -- _komorebi_ is aware of the device and associated
|
||||
window handles.
|
||||
|
||||
2. **Let your display go to sleep.**
|
||||
|
||||
Simply turning the monitor off is not enough to reproduce the problem; you must
|
||||
let Windows turn off the display itself.
|
||||
|
||||
To avoid waiting an eternity:
|
||||
|
||||
- _Control Panel_ -> _Hardware and Sound_ -> _Power Options_ -> _Edit Plan
|
||||
Settings_
|
||||
|
||||
_Turn off the display: 1 minute_
|
||||
|
||||
Allow a minute for the display to reset, then once it actually shuts off
|
||||
allow for any additional time as prompted by your monitor for the cycle to
|
||||
complete.
|
||||
|
||||
3. **Wake your display again** by pressing any key.
|
||||
|
||||
_komorebi_ should now be unresponsive.
|
||||
|
||||
4. **`komorebic state`**
|
||||
|
||||
Don't stop _komorebi_ just yet.
|
||||
|
||||
Since it's unresponsive, you can open another shell instead to execute the above command.
|
||||
|
||||
```json
|
||||
{
|
||||
"monitors": {
|
||||
"elements": [
|
||||
{
|
||||
"id": 65537,
|
||||
"name": "DISPLAY1",
|
||||
"device": null,
|
||||
"device_id": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can see the _komorebi_ state is no longer associated with the previous
|
||||
device: `null`, suggesting an issue when the display resumes from a suspended
|
||||
state.
|
||||
@@ -10,6 +10,9 @@ alt + shift + o : komorebic reload-configuration
|
||||
# alt + f : if ($wshell.AppActivate('Firefox') -eq $False) { start firefox }
|
||||
# alt + b : if ($wshell.AppActivate('Chrome') -eq $False) { start chrome }
|
||||
|
||||
alt + q : komorebic close
|
||||
alt + m : komorebic minimize
|
||||
|
||||
# Focus windows
|
||||
alt + h : komorebic focus left
|
||||
alt + j : komorebic focus down
|
||||
@@ -56,8 +59,18 @@ alt + y : komorebic flip-layout vertical
|
||||
alt + 1 : komorebic focus-workspace 0
|
||||
alt + 2 : komorebic focus-workspace 1
|
||||
alt + 3 : komorebic focus-workspace 2
|
||||
alt + 4 : komorebic focus-workspace 3
|
||||
alt + 5 : komorebic focus-workspace 4
|
||||
alt + 6 : komorebic focus-workspace 5
|
||||
alt + 7 : komorebic focus-workspace 6
|
||||
alt + 8 : komorebic focus-workspace 7
|
||||
|
||||
# Move windows across workspaces
|
||||
alt + shift + 1 : komorebic move-to-workspace 0
|
||||
alt + shift + 2 : komorebic move-to-workspace 1
|
||||
alt + shift + 3 : komorebic move-to-workspace 2
|
||||
alt + shift + 4 : komorebic move-to-workspace 3
|
||||
alt + shift + 5 : komorebic move-to-workspace 4
|
||||
alt + shift + 6 : komorebic move-to-workspace 5
|
||||
alt + shift + 7 : komorebic move-to-workspace 6
|
||||
alt + shift + 8 : komorebic move-to-workspace 7
|
||||
|
||||
39
justfile
39
justfile
@@ -7,29 +7,24 @@ clean:
|
||||
fmt:
|
||||
cargo +nightly fmt
|
||||
cargo +stable clippy
|
||||
prettier --write README.md
|
||||
prettier --write .goreleaser.yml
|
||||
prettier --write .github/ISSUE_TEMPLATE/bug_report.yml
|
||||
prettier --write .github/ISSUE_TEMPLATE/config.yml
|
||||
prettier --write .github/ISSUE_TEMPLATE/feature_request.yml
|
||||
prettier --write .github/dependabot.yml
|
||||
prettier --write .github/FUNDING.yml
|
||||
prettier --write .github/workflows/windows.yaml
|
||||
|
||||
install-targets *targets:
|
||||
"{{ targets }}" -split ' ' | ForEach-Object { just install-target $_ }
|
||||
|
||||
install-target target:
|
||||
cargo +stable install --path {{ target }} --locked
|
||||
|
||||
prepare:
|
||||
komorebic ahk-asc '~/.config/komorebi/applications.yaml'
|
||||
komorebic pwsh-asc '~/.config/komorebi/applications.yaml'
|
||||
cat '~/.config/komorebi/komorebi.generated.ps1' >komorebi.generated.ps1
|
||||
cat '~/.config/komorebi/komorebi.generated.ahk' >komorebi.generated.ahk
|
||||
|
||||
install:
|
||||
just install-target komorebic
|
||||
just install-target komorebic-no-console
|
||||
just install-target komorebi
|
||||
just install-targets komorebic komorebic-no-console komorebi komorebi-bar komorebi-gui
|
||||
|
||||
run:
|
||||
just install-target komorebic
|
||||
cargo +stable run --bin komorebi --locked
|
||||
run target:
|
||||
cargo +stable run --bin {{ target }} --locked
|
||||
|
||||
warn $RUST_LOG="warn":
|
||||
just run
|
||||
@@ -44,17 +39,19 @@ trace $RUST_LOG="trace":
|
||||
just run
|
||||
|
||||
deadlock $RUST_LOG="trace":
|
||||
just install-komorebic
|
||||
cargo +stable run --bin komorebi --locked --features deadlock_detection
|
||||
|
||||
docgen:
|
||||
komorebic docgen
|
||||
cargo run --package komorebic -- docgen
|
||||
Get-ChildItem -Path "docs/cli" -Recurse -File | ForEach-Object { (Get-Content $_.FullName) -replace 'Usage: ', 'Usage: komorebic.exe ' | Set-Content $_.FullName }
|
||||
|
||||
exampledocs:
|
||||
cp whkdrc.sample docs/whkdrc.sample
|
||||
cp komorebi.example.json docs/komorebi.example.json
|
||||
|
||||
schemagen:
|
||||
komorebic static-config-schema > schema.json
|
||||
cargo run --package komorebic -- static-config-schema > schema.json
|
||||
cargo run --package komorebic -- application-specific-configuration-schema > schema.asc.json
|
||||
cargo run --package komorebi-bar -- --schema > schema.bar.json
|
||||
generate-schema-doc .\schema.json --config template_name=js_offline --config minify=false .\static-config-docs\
|
||||
|
||||
generate-schema-doc .\schema.bar.json --config template_name=js_offline --config minify=false .\bar-config-docs\
|
||||
|
||||
rm -Force .\bar-config-docs\schema.html
|
||||
mv .\bar-config-docs\schema.bar.html .\bar-config-docs\schema.html
|
||||
|
||||
37
komorebi-bar/Cargo.toml
Normal file
37
komorebi-bar/Cargo.toml
Normal file
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "komorebi-bar"
|
||||
version = "0.1.30"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
komorebi-client = { path = "../komorebi-client" }
|
||||
komorebi-themes = { path = "../komorebi-themes" }
|
||||
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
color-eyre = { workspace = true }
|
||||
crossbeam-channel = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
dunce = { workspace = true }
|
||||
eframe = { workspace = true }
|
||||
egui-phosphor = "0.7"
|
||||
font-loader = "0.11"
|
||||
hotwatch = { workspace = true }
|
||||
image = "0.25"
|
||||
netdev = "0.31"
|
||||
num = "0.4"
|
||||
num-derive = "0.4"
|
||||
num-traits = "0.2"
|
||||
random_word = { version = "0.4", features = ["en"] }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
starship-battery = "0.10"
|
||||
sysinfo = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tracing-appender = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
windows-icons = { git = "https://github.com/LGUG2Z/windows-icons", rev = "d67cc9920aa9b4883393e411fb4fa2ddd4c498b5" }
|
||||
458
komorebi-bar/src/bar.rs
Normal file
458
komorebi-bar/src/bar.rs
Normal file
@@ -0,0 +1,458 @@
|
||||
use crate::config::KomobarConfig;
|
||||
use crate::config::KomobarTheme;
|
||||
use crate::config::Position;
|
||||
use crate::config::PositionConfig;
|
||||
use crate::komorebi::Komorebi;
|
||||
use crate::komorebi::KomorebiNotificationState;
|
||||
use crate::process_hwnd;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::widget::WidgetConfig;
|
||||
use crate::BAR_HEIGHT;
|
||||
use crate::MAX_LABEL_WIDTH;
|
||||
use crate::MONITOR_LEFT;
|
||||
use crate::MONITOR_RIGHT;
|
||||
use crate::MONITOR_TOP;
|
||||
use crossbeam_channel::Receiver;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::CentralPanel;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontData;
|
||||
use eframe::egui::FontDefinitions;
|
||||
use eframe::egui::FontFamily;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Frame;
|
||||
use eframe::egui::Layout;
|
||||
use eframe::egui::Margin;
|
||||
use eframe::egui::Style;
|
||||
use eframe::egui::TextStyle;
|
||||
use font_loader::system_fonts;
|
||||
use font_loader::system_fonts::FontPropertyBuilder;
|
||||
use komorebi_client::KomorebiTheme;
|
||||
use komorebi_themes::catppuccin_egui;
|
||||
use komorebi_themes::Base16Value;
|
||||
use komorebi_themes::Catppuccin;
|
||||
use komorebi_themes::CatppuccinValue;
|
||||
use std::cell::RefCell;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Komobar {
|
||||
pub config: Arc<KomobarConfig>,
|
||||
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||
pub left_widgets: Vec<Box<dyn BarWidget>>,
|
||||
pub right_widgets: Vec<Box<dyn BarWidget>>,
|
||||
pub rx_gui: Receiver<komorebi_client::Notification>,
|
||||
pub rx_config: Receiver<KomobarConfig>,
|
||||
pub bg_color: Rc<RefCell<Color32>>,
|
||||
pub scale_factor: f32,
|
||||
}
|
||||
|
||||
pub fn apply_theme(ctx: &Context, theme: KomobarTheme, bg_color: Rc<RefCell<Color32>>) {
|
||||
match theme {
|
||||
KomobarTheme::Catppuccin {
|
||||
name: catppuccin,
|
||||
accent: catppuccin_value,
|
||||
} => match catppuccin {
|
||||
Catppuccin::Frappe => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::FRAPPE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::FRAPPE.base);
|
||||
}
|
||||
Catppuccin::Latte => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::LATTE);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::LATTE.base);
|
||||
}
|
||||
Catppuccin::Macchiato => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MACCHIATO);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MACCHIATO.base);
|
||||
}
|
||||
Catppuccin::Mocha => {
|
||||
catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA);
|
||||
let catppuccin_value = catppuccin_value.unwrap_or_default();
|
||||
let accent = catppuccin_value.color32(catppuccin.as_theme());
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
style.visuals.override_text_color = None;
|
||||
});
|
||||
|
||||
bg_color.replace(catppuccin_egui::MOCHA.base);
|
||||
}
|
||||
},
|
||||
KomobarTheme::Base16 {
|
||||
name: base16,
|
||||
accent: base16_value,
|
||||
} => {
|
||||
ctx.set_style(base16.style());
|
||||
let base16_value = base16_value.unwrap_or_default();
|
||||
let accent = base16_value.color32(base16);
|
||||
|
||||
ctx.style_mut(|style| {
|
||||
style.visuals.selection.stroke.color = accent;
|
||||
style.visuals.widgets.hovered.fg_stroke.color = accent;
|
||||
style.visuals.widgets.active.fg_stroke.color = accent;
|
||||
});
|
||||
|
||||
bg_color.replace(base16.background());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Komobar {
|
||||
pub fn apply_config(
|
||||
&mut self,
|
||||
ctx: &Context,
|
||||
config: &KomobarConfig,
|
||||
previous_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||
) {
|
||||
MAX_LABEL_WIDTH.store(
|
||||
config.max_label_width.unwrap_or(400.0) as i32,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
if let Some(font_family) = &config.font_family {
|
||||
tracing::info!("attempting to add custom font family: {font_family}");
|
||||
Self::add_custom_font(ctx, font_family);
|
||||
}
|
||||
|
||||
let position = config.position.clone().unwrap_or(PositionConfig {
|
||||
start: Some(Position {
|
||||
x: MONITOR_LEFT.load(Ordering::SeqCst) as f32,
|
||||
y: MONITOR_TOP.load(Ordering::SeqCst) as f32,
|
||||
}),
|
||||
end: Some(Position {
|
||||
x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32,
|
||||
y: BAR_HEIGHT,
|
||||
}),
|
||||
});
|
||||
|
||||
if let Some(hwnd) = process_hwnd() {
|
||||
let start = position.start.unwrap_or(Position {
|
||||
x: MONITOR_LEFT.load(Ordering::SeqCst) as f32,
|
||||
y: MONITOR_TOP.load(Ordering::SeqCst) as f32,
|
||||
});
|
||||
|
||||
let end = position.end.unwrap_or(Position {
|
||||
x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32,
|
||||
y: BAR_HEIGHT,
|
||||
});
|
||||
|
||||
let rect = komorebi_client::Rect {
|
||||
left: start.x as i32,
|
||||
top: start.y as i32,
|
||||
right: end.x as i32,
|
||||
bottom: end.y as i32,
|
||||
};
|
||||
|
||||
let window = komorebi_client::Window::from(hwnd);
|
||||
match window.set_position(&rect, false) {
|
||||
Ok(_) => {
|
||||
tracing::info!("updated bar position");
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{}", error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match config.theme {
|
||||
Some(theme) => {
|
||||
apply_theme(ctx, theme, self.bg_color.clone());
|
||||
}
|
||||
None => {
|
||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!("$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let config = home_dir.join("komorebi.json");
|
||||
match komorebi_client::StaticConfig::read(&config) {
|
||||
Ok(config) => {
|
||||
if let Some(theme) = config.theme {
|
||||
apply_theme(ctx, KomobarTheme::from(theme), self.bg_color.clone());
|
||||
|
||||
let stack_accent = match theme {
|
||||
KomorebiTheme::Catppuccin {
|
||||
name, stack_border, ..
|
||||
} => stack_border
|
||||
.unwrap_or(CatppuccinValue::Green)
|
||||
.color32(name.as_theme()),
|
||||
KomorebiTheme::Base16 {
|
||||
name, stack_border, ..
|
||||
} => stack_border.unwrap_or(Base16Value::Base0B).color32(name),
|
||||
};
|
||||
|
||||
if let Some(state) = &self.komorebi_notification_state {
|
||||
state.borrow_mut().stack_accent = Some(stack_accent);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
ctx.set_style(Style::default());
|
||||
self.bg_color.replace(Style::default().visuals.panel_fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(font_size) = &config.font_size {
|
||||
tracing::info!("attempting to set custom font size: {font_size}");
|
||||
Self::set_font_size(ctx, *font_size);
|
||||
}
|
||||
|
||||
let mut komorebi_widget = None;
|
||||
let mut komorebi_widget_idx = None;
|
||||
let mut komorebi_notification_state = previous_notification_state;
|
||||
let mut side = None;
|
||||
|
||||
for (idx, widget_config) in config.left_widgets.iter().enumerate() {
|
||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||
komorebi_widget = Some(Komorebi::from(config));
|
||||
komorebi_widget_idx = Some(idx);
|
||||
side = Some(Side::Left);
|
||||
}
|
||||
}
|
||||
|
||||
for (idx, widget_config) in config.right_widgets.iter().enumerate() {
|
||||
if let WidgetConfig::Komorebi(config) = widget_config {
|
||||
komorebi_widget = Some(Komorebi::from(config));
|
||||
komorebi_widget_idx = Some(idx);
|
||||
side = Some(Side::Right);
|
||||
}
|
||||
}
|
||||
|
||||
let mut left_widgets = config
|
||||
.left_widgets
|
||||
.iter()
|
||||
.map(|config| config.as_boxed_bar_widget())
|
||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||
|
||||
let mut right_widgets = config
|
||||
.right_widgets
|
||||
.iter()
|
||||
.map(|config| config.as_boxed_bar_widget())
|
||||
.collect::<Vec<Box<dyn BarWidget>>>();
|
||||
|
||||
if let (Some(idx), Some(mut widget), Some(side)) =
|
||||
(komorebi_widget_idx, komorebi_widget, side)
|
||||
{
|
||||
match komorebi_notification_state {
|
||||
None => {
|
||||
komorebi_notification_state = Some(widget.komorebi_notification_state.clone());
|
||||
}
|
||||
Some(ref previous) => {
|
||||
previous
|
||||
.borrow_mut()
|
||||
.update_from_config(&widget.komorebi_notification_state.borrow());
|
||||
|
||||
widget.komorebi_notification_state = previous.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let boxed: Box<dyn BarWidget> = Box::new(widget);
|
||||
match side {
|
||||
Side::Left => left_widgets[idx] = boxed,
|
||||
Side::Right => right_widgets[idx] = boxed,
|
||||
}
|
||||
}
|
||||
|
||||
right_widgets.reverse();
|
||||
|
||||
self.left_widgets = left_widgets;
|
||||
self.right_widgets = right_widgets;
|
||||
|
||||
tracing::info!("widget configuration options applied");
|
||||
|
||||
self.komorebi_notification_state = komorebi_notification_state;
|
||||
}
|
||||
pub fn new(
|
||||
cc: &eframe::CreationContext<'_>,
|
||||
rx_gui: Receiver<komorebi_client::Notification>,
|
||||
rx_config: Receiver<KomobarConfig>,
|
||||
config: Arc<KomobarConfig>,
|
||||
) -> Self {
|
||||
let mut komobar = Self {
|
||||
config: config.clone(),
|
||||
komorebi_notification_state: None,
|
||||
left_widgets: vec![],
|
||||
right_widgets: vec![],
|
||||
rx_gui,
|
||||
rx_config,
|
||||
bg_color: Rc::new(RefCell::new(Style::default().visuals.panel_fill)),
|
||||
scale_factor: cc.egui_ctx.native_pixels_per_point().unwrap_or(1.0),
|
||||
};
|
||||
|
||||
komobar.apply_config(&cc.egui_ctx, &config, None);
|
||||
// needs a double apply the first time for some reason
|
||||
komobar.apply_config(&cc.egui_ctx, &config, None);
|
||||
|
||||
komobar
|
||||
}
|
||||
|
||||
fn set_font_size(ctx: &Context, font_size: f32) {
|
||||
ctx.style_mut(|style| {
|
||||
style.text_styles = [
|
||||
(TextStyle::Small, FontId::new(9.0, FontFamily::Proportional)),
|
||||
(
|
||||
TextStyle::Body,
|
||||
FontId::new(font_size, FontFamily::Proportional),
|
||||
),
|
||||
(
|
||||
TextStyle::Button,
|
||||
FontId::new(font_size, FontFamily::Proportional),
|
||||
),
|
||||
(
|
||||
TextStyle::Heading,
|
||||
FontId::new(18.0, FontFamily::Proportional),
|
||||
),
|
||||
(
|
||||
TextStyle::Monospace,
|
||||
FontId::new(font_size, FontFamily::Monospace),
|
||||
),
|
||||
]
|
||||
.into();
|
||||
});
|
||||
}
|
||||
|
||||
fn add_custom_font(ctx: &Context, name: &str) {
|
||||
let mut fonts = FontDefinitions::default();
|
||||
egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular);
|
||||
|
||||
let property = FontPropertyBuilder::new().family(name).build();
|
||||
|
||||
if let Some((font, _)) = system_fonts::get(&property) {
|
||||
fonts
|
||||
.font_data
|
||||
.insert(name.to_owned(), FontData::from_owned(font));
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Proportional)
|
||||
.or_default()
|
||||
.insert(0, name.to_owned());
|
||||
|
||||
fonts
|
||||
.families
|
||||
.entry(FontFamily::Monospace)
|
||||
.or_default()
|
||||
.push(name.to_owned());
|
||||
|
||||
// Tell egui to use these fonts:
|
||||
ctx.set_fonts(fonts);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl eframe::App for Komobar {
|
||||
// TODO: I think this is needed for transparency??
|
||||
// fn clear_color(&self, _visuals: &Visuals) -> [f32; 4] {
|
||||
// egui::Rgba::TRANSPARENT.to_array()
|
||||
// let mut background = Color32::from_gray(18).to_normalized_gamma_f32();
|
||||
// background[3] = 0.9;
|
||||
// background
|
||||
// }
|
||||
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||
if self.scale_factor != ctx.native_pixels_per_point().unwrap_or(1.0) {
|
||||
self.scale_factor = ctx.native_pixels_per_point().unwrap_or(1.0);
|
||||
self.apply_config(
|
||||
ctx,
|
||||
&self.config.clone(),
|
||||
self.komorebi_notification_state.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok(updated_config) = self.rx_config.try_recv() {
|
||||
self.apply_config(
|
||||
ctx,
|
||||
&updated_config,
|
||||
self.komorebi_notification_state.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(komorebi_notification_state) = &self.komorebi_notification_state {
|
||||
komorebi_notification_state
|
||||
.borrow_mut()
|
||||
.handle_notification(
|
||||
ctx,
|
||||
self.config.monitor.index,
|
||||
self.rx_gui.clone(),
|
||||
self.bg_color.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
let frame = if let Some(frame) = &self.config.frame {
|
||||
Frame::none()
|
||||
.inner_margin(Margin::symmetric(
|
||||
frame.inner_margin.x,
|
||||
frame.inner_margin.y,
|
||||
))
|
||||
.fill(*self.bg_color.borrow())
|
||||
} else {
|
||||
Frame::none().fill(*self.bg_color.borrow())
|
||||
};
|
||||
|
||||
CentralPanel::default().frame(frame).show(ctx, |ui| {
|
||||
ui.horizontal_centered(|ui| {
|
||||
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||
for w in &mut self.left_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
});
|
||||
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
for w in &mut self.right_widgets {
|
||||
w.render(ctx, ui);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Side {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
160
komorebi-bar/src/battery.rs
Normal file
160
komorebi-bar/src/battery.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use starship_battery::units::ratio::percent;
|
||||
use starship_battery::Manager;
|
||||
use starship_battery::State;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct BatteryConfig {
|
||||
/// Enable the Battery widget
|
||||
pub enable: bool,
|
||||
/// Data refresh interval (default: 10 seconds)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
}
|
||||
|
||||
impl From<BatteryConfig> for Battery {
|
||||
fn from(value: BatteryConfig) -> Self {
|
||||
let manager = Manager::new().unwrap();
|
||||
let mut last_state = String::new();
|
||||
let mut state = None;
|
||||
let prefix = value.label_prefix.unwrap_or(LabelPrefix::Icon);
|
||||
|
||||
if let Ok(mut batteries) = manager.batteries() {
|
||||
if let Some(Ok(first)) = batteries.nth(0) {
|
||||
let percentage = first.state_of_charge().get::<percent>();
|
||||
match first.state() {
|
||||
State::Charging => state = Some(BatteryState::Charging),
|
||||
State::Discharging => state = Some(BatteryState::Discharging),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
last_state = match prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("BAT: {percentage:.0}%")
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{percentage:.0}%"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
manager,
|
||||
last_state,
|
||||
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
||||
label_prefix: prefix,
|
||||
state: state.unwrap_or(BatteryState::Discharging),
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BatteryState {
|
||||
Charging,
|
||||
Discharging,
|
||||
}
|
||||
|
||||
pub struct Battery {
|
||||
pub enable: bool,
|
||||
manager: Manager,
|
||||
pub state: BatteryState,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
last_state: String,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Battery {
|
||||
fn output(&mut self) -> String {
|
||||
let mut output = self.last_state.clone();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
output.clear();
|
||||
|
||||
if let Ok(mut batteries) = self.manager.batteries() {
|
||||
if let Some(Ok(first)) = batteries.nth(0) {
|
||||
let percentage = first.state_of_charge().get::<percent>();
|
||||
match first.state() {
|
||||
State::Charging => self.state = BatteryState::Charging,
|
||||
State::Discharging => self.state = BatteryState::Discharging,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
output = match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("BAT: {percentage:.0}%")
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{percentage:.0}%"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.last_state.clone_from(&output);
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Battery {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.is_empty() {
|
||||
let emoji = match self.state {
|
||||
BatteryState::Charging => egui_phosphor::regular::BATTERY_CHARGING,
|
||||
BatteryState::Discharging => egui_phosphor::regular::BATTERY_FULL,
|
||||
};
|
||||
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => emoji.to_string(),
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
|
||||
);
|
||||
|
||||
ui.add(
|
||||
Label::new(layout_job)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
);
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
komorebi-bar/src/config.rs
Normal file
178
komorebi-bar/src/config.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use crate::widget::WidgetConfig;
|
||||
use eframe::egui::Pos2;
|
||||
use eframe::egui::TextBuffer;
|
||||
use eframe::egui::Vec2;
|
||||
use komorebi_client::KomorebiTheme;
|
||||
use komorebi_client::Rect;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.30`
|
||||
pub struct KomobarConfig {
|
||||
/// Bar positioning options
|
||||
#[serde(alias = "viewport")]
|
||||
pub position: Option<PositionConfig>,
|
||||
/// Frame options (see: https://docs.rs/egui/latest/egui/containers/struct.Frame.html)
|
||||
pub frame: Option<FrameConfig>,
|
||||
/// Monitor options
|
||||
pub monitor: MonitorConfig,
|
||||
/// Font family
|
||||
pub font_family: Option<String>,
|
||||
/// Font size (default: 12.5)
|
||||
pub font_size: Option<f32>,
|
||||
/// Max label width before text truncation (default: 400.0)
|
||||
pub max_label_width: Option<f32>,
|
||||
/// Theme
|
||||
pub theme: Option<KomobarTheme>,
|
||||
/// Left side widgets (ordered left-to-right)
|
||||
pub left_widgets: Vec<WidgetConfig>,
|
||||
/// Right side widgets (ordered left-to-right)
|
||||
pub right_widgets: Vec<WidgetConfig>,
|
||||
}
|
||||
|
||||
impl KomobarConfig {
|
||||
pub fn aliases(raw: &str) {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("position", ["viewport"]);
|
||||
map.insert("end", ["inner_frame"]);
|
||||
|
||||
let mut display = false;
|
||||
|
||||
for aliases in map.values() {
|
||||
for a in aliases {
|
||||
if raw.contains(a) {
|
||||
display = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if display {
|
||||
println!("\nYour bar configuration file contains some options that have been renamed or deprecated:\n");
|
||||
for (canonical, aliases) in map {
|
||||
for alias in aliases {
|
||||
if raw.contains(alias) {
|
||||
println!(r#""{alias}" is now "{canonical}""#);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct PositionConfig {
|
||||
/// The desired starting position of the bar (0,0 = top left of the screen)
|
||||
#[serde(alias = "position")]
|
||||
pub start: Option<Position>,
|
||||
/// The desired size of the bar from the starting position (usually monitor width x desired height)
|
||||
#[serde(alias = "inner_size")]
|
||||
pub end: Option<Position>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct FrameConfig {
|
||||
/// Margin inside the painted frame
|
||||
pub inner_margin: Position,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MonitorConfig {
|
||||
/// Komorebi monitor index of the monitor on which to render the bar
|
||||
pub index: usize,
|
||||
/// Automatically apply a work area offset for this monitor to accommodate the bar
|
||||
pub work_area_offset: Option<Rect>,
|
||||
}
|
||||
|
||||
impl KomobarConfig {
|
||||
pub fn read(path: &PathBuf) -> color_eyre::Result<Self> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let mut value: Self = match path.extension().unwrap().to_string_lossy().as_str() {
|
||||
"json" => serde_json::from_str(&content)?,
|
||||
_ => panic!("unsupported format"),
|
||||
};
|
||||
|
||||
if value.frame.is_none() {
|
||||
value.frame = Some(FrameConfig {
|
||||
inner_margin: Position { x: 10.0, y: 10.0 },
|
||||
});
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct Position {
|
||||
/// X coordinate
|
||||
pub x: f32,
|
||||
/// Y coordinate
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
impl From<Position> for Vec2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Position> for Pos2 {
|
||||
fn from(value: Position) -> Self {
|
||||
Self {
|
||||
x: value.x,
|
||||
y: value.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(tag = "palette")]
|
||||
pub enum KomobarTheme {
|
||||
/// A theme from catppuccin-egui
|
||||
Catppuccin {
|
||||
name: komorebi_themes::Catppuccin,
|
||||
accent: Option<komorebi_themes::CatppuccinValue>,
|
||||
},
|
||||
/// A theme from base16-egui-themes
|
||||
Base16 {
|
||||
name: komorebi_themes::Base16,
|
||||
accent: Option<komorebi_themes::Base16Value>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<KomorebiTheme> for KomobarTheme {
|
||||
fn from(value: KomorebiTheme) -> Self {
|
||||
match value {
|
||||
KomorebiTheme::Catppuccin {
|
||||
name, bar_accent, ..
|
||||
} => Self::Catppuccin {
|
||||
name,
|
||||
accent: bar_accent,
|
||||
},
|
||||
KomorebiTheme::Base16 {
|
||||
name, bar_accent, ..
|
||||
} => Self::Base16 {
|
||||
name,
|
||||
accent: bar_accent,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum LabelPrefix {
|
||||
/// Show no prefix
|
||||
None,
|
||||
/// Show an icon
|
||||
Icon,
|
||||
/// Show text
|
||||
Text,
|
||||
/// Show an icon and text
|
||||
IconAndText,
|
||||
}
|
||||
120
komorebi-bar/src/cpu.rs
Normal file
120
komorebi-bar/src/cpu.rs
Normal file
@@ -0,0 +1,120 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::RefreshKind;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CpuConfig {
|
||||
/// Enable the Cpu widget
|
||||
pub enable: bool,
|
||||
/// Data refresh interval (default: 10 seconds)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
}
|
||||
|
||||
impl From<CpuConfig> for Cpu {
|
||||
fn from(value: CpuConfig) -> Self {
|
||||
let mut system =
|
||||
System::new_with_specifics(RefreshKind::default().without_memory().without_processes());
|
||||
|
||||
system.refresh_cpu_usage();
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
system,
|
||||
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Cpu {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Cpu {
|
||||
fn output(&mut self) -> String {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.system.refresh_cpu_usage();
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
let used = self.system.global_cpu_usage();
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => format!("CPU: {:.0}%", used),
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{:.0}%", used),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Cpu {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.is_empty() {
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
egui_phosphor::regular::CPU.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
|
||||
);
|
||||
|
||||
if ui
|
||||
.add(
|
||||
Label::new(layout_job)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
137
komorebi-bar/src/date.rs
Normal file
137
komorebi-bar/src/date.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use eframe::egui::WidgetText;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct DateConfig {
|
||||
/// Enable the Date widget
|
||||
pub enable: bool,
|
||||
/// Set the Date format
|
||||
pub format: DateFormat,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
}
|
||||
|
||||
impl From<DateConfig> for Date {
|
||||
fn from(value: DateConfig) -> Self {
|
||||
Self {
|
||||
enable: value.enable,
|
||||
format: value.format,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::Icon),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub enum DateFormat {
|
||||
/// Month/Date/Year format (09/08/24)
|
||||
MonthDateYear,
|
||||
/// Year-Month-Date format (2024-09-08)
|
||||
YearMonthDate,
|
||||
/// Date-Month-Year format (8-Sep-2024)
|
||||
DateMonthYear,
|
||||
/// Day Date Month Year format (8 September 2024)
|
||||
DayDateMonthYear,
|
||||
/// Custom format (https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl DateFormat {
|
||||
pub fn next(&mut self) {
|
||||
match self {
|
||||
DateFormat::MonthDateYear => *self = Self::YearMonthDate,
|
||||
DateFormat::YearMonthDate => *self = Self::DateMonthYear,
|
||||
DateFormat::DateMonthYear => *self = Self::DayDateMonthYear,
|
||||
DateFormat::DayDateMonthYear => *self = Self::MonthDateYear,
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
fn fmt_string(&self) -> String {
|
||||
match self {
|
||||
DateFormat::MonthDateYear => String::from("%D"),
|
||||
DateFormat::YearMonthDate => String::from("%F"),
|
||||
DateFormat::DateMonthYear => String::from("%v"),
|
||||
DateFormat::DayDateMonthYear => String::from("%A %e %B %Y"),
|
||||
DateFormat::Custom(custom) => custom.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Date {
|
||||
pub enable: bool,
|
||||
pub format: DateFormat,
|
||||
label_prefix: LabelPrefix,
|
||||
}
|
||||
|
||||
impl Date {
|
||||
fn output(&mut self) -> String {
|
||||
chrono::Local::now()
|
||||
.format(&self.format.fmt_string())
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Date {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let mut output = self.output();
|
||||
if !output.is_empty() {
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
egui_phosphor::regular::CALENDAR_DOTS.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
if let LabelPrefix::Text | LabelPrefix::IconAndText = self.label_prefix {
|
||||
output.insert_str(0, "DATE: ");
|
||||
}
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
|
||||
);
|
||||
|
||||
if ui
|
||||
.add(
|
||||
Label::new(WidgetText::LayoutJob(layout_job.clone()))
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
self.format.next()
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
546
komorebi-bar/src/komorebi.rs
Normal file
546
komorebi-bar/src/komorebi.rs
Normal file
@@ -0,0 +1,546 @@
|
||||
use crate::bar::apply_theme;
|
||||
use crate::config::KomobarTheme;
|
||||
use crate::ui::CustomUi;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::MAX_LABEL_WIDTH;
|
||||
use crate::WIDGET_SPACING;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::TryRecvError;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Color32;
|
||||
use eframe::egui::ColorImage;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Image;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::SelectableLabel;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::TextureHandle;
|
||||
use eframe::egui::TextureOptions;
|
||||
use eframe::egui::Ui;
|
||||
use eframe::egui::Vec2;
|
||||
use image::RgbaImage;
|
||||
use komorebi_client::CycleDirection;
|
||||
use komorebi_client::NotificationEvent;
|
||||
use komorebi_client::Rect;
|
||||
use komorebi_client::SocketMessage;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiConfig {
|
||||
/// Configure the Workspaces widget
|
||||
pub workspaces: KomorebiWorkspacesConfig,
|
||||
/// Configure the Layout widget
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
/// Configure the Focused Window widget
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
/// Configure the Configuration Switcher widget
|
||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiWorkspacesConfig {
|
||||
/// Enable the Komorebi Workspaces widget
|
||||
pub enable: bool,
|
||||
/// Hide workspaces without any windows
|
||||
pub hide_empty_workspaces: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiLayoutConfig {
|
||||
/// Enable the Komorebi Layout widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiFocusedWindowConfig {
|
||||
/// Enable the Komorebi Focused Window widget
|
||||
pub enable: bool,
|
||||
/// Show the icon of the currently focused window
|
||||
pub show_icon: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct KomorebiConfigurationSwitcherConfig {
|
||||
/// Enable the Komorebi Configurations widget
|
||||
pub enable: bool,
|
||||
/// A map of display friendly name => path to configuration.json
|
||||
pub configurations: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl From<&KomorebiConfig> for Komorebi {
|
||||
fn from(value: &KomorebiConfig) -> Self {
|
||||
let configuration_switcher =
|
||||
if let Some(configuration_switcher) = &value.configuration_switcher {
|
||||
let mut configuration_switcher = configuration_switcher.clone();
|
||||
for (_, location) in configuration_switcher.configurations.iter_mut() {
|
||||
*location = dunce::simplified(&PathBuf::from(location.clone()))
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
}
|
||||
Some(configuration_switcher)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Self {
|
||||
komorebi_notification_state: Rc::new(RefCell::new(KomorebiNotificationState {
|
||||
selected_workspace: String::new(),
|
||||
layout: KomorebiLayout::Default(komorebi_client::DefaultLayout::BSP),
|
||||
workspaces: vec![],
|
||||
hide_empty_workspaces: value.workspaces.hide_empty_workspaces,
|
||||
mouse_follows_focus: true,
|
||||
work_area_offset: None,
|
||||
focused_container_information: (vec![], vec![], 0),
|
||||
stack_accent: None,
|
||||
})),
|
||||
workspaces: value.workspaces,
|
||||
layout: value.layout,
|
||||
focused_window: value.focused_window,
|
||||
configuration_switcher,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Komorebi {
|
||||
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||
pub workspaces: KomorebiWorkspacesConfig,
|
||||
pub layout: Option<KomorebiLayoutConfig>,
|
||||
pub focused_window: Option<KomorebiFocusedWindowConfig>,
|
||||
pub configuration_switcher: Option<KomorebiConfigurationSwitcherConfig>,
|
||||
}
|
||||
|
||||
impl BarWidget for Komorebi {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
|
||||
|
||||
if self.workspaces.enable {
|
||||
let mut update = None;
|
||||
|
||||
for (i, ws) in komorebi_notification_state.workspaces.iter().enumerate() {
|
||||
if ui
|
||||
.add(SelectableLabel::new(
|
||||
komorebi_notification_state.selected_workspace.eq(ws),
|
||||
ws.to_string(),
|
||||
))
|
||||
.clicked()
|
||||
{
|
||||
update = Some(ws.to_string());
|
||||
let mut proceed = true;
|
||||
|
||||
if komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(false))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: MouseFollowsFocus");
|
||||
proceed = false;
|
||||
}
|
||||
|
||||
if proceed
|
||||
&& komorebi_client::send_message(&SocketMessage::FocusWorkspaceNumber(i))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: FocusWorkspaceNumber");
|
||||
proceed = false;
|
||||
}
|
||||
|
||||
if proceed
|
||||
&& komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
||||
komorebi_notification_state.mouse_follows_focus,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: MouseFollowsFocus");
|
||||
proceed = false;
|
||||
}
|
||||
|
||||
if proceed
|
||||
&& komorebi_client::send_message(&SocketMessage::RetileWithResizeDimensions)
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: Retile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(update) = update {
|
||||
komorebi_notification_state.selected_workspace = update;
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
|
||||
if let Some(layout) = self.layout {
|
||||
if layout.enable {
|
||||
if ui
|
||||
.add(
|
||||
Label::new(komorebi_notification_state.layout.to_string())
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
match komorebi_notification_state.layout {
|
||||
KomorebiLayout::Default(_) => {
|
||||
if komorebi_client::send_message(&SocketMessage::CycleLayout(
|
||||
CycleDirection::Next,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: CycleLayout");
|
||||
}
|
||||
}
|
||||
KomorebiLayout::Floating => {
|
||||
if komorebi_client::send_message(&SocketMessage::ToggleTiling).is_err()
|
||||
{
|
||||
tracing::error!("could not send message to komorebi: ToggleTiling");
|
||||
}
|
||||
}
|
||||
KomorebiLayout::Paused => {
|
||||
if komorebi_client::send_message(&SocketMessage::TogglePause).is_err() {
|
||||
tracing::error!("could not send message to komorebi: TogglePause");
|
||||
}
|
||||
}
|
||||
KomorebiLayout::Custom => {}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(configuration_switcher) = &self.configuration_switcher {
|
||||
if configuration_switcher.enable {
|
||||
for (name, location) in configuration_switcher.configurations.iter() {
|
||||
let path = PathBuf::from(location);
|
||||
if path.is_file()
|
||||
&& ui
|
||||
.add(Label::new(name).selectable(false).sense(Sense::click()))
|
||||
.clicked()
|
||||
{
|
||||
let canonicalized = dunce::canonicalize(path.clone()).unwrap_or(path);
|
||||
let mut proceed = true;
|
||||
if komorebi_client::send_message(&SocketMessage::ReplaceConfiguration(
|
||||
canonicalized,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: ReplaceConfiguration"
|
||||
);
|
||||
proceed = false;
|
||||
}
|
||||
|
||||
if let Some(rect) = komorebi_notification_state.work_area_offset {
|
||||
if proceed {
|
||||
match komorebi_client::send_query(&SocketMessage::Query(
|
||||
komorebi_client::StateQuery::FocusedMonitorIndex,
|
||||
)) {
|
||||
Ok(idx) => {
|
||||
if let Ok(monitor_idx) = idx.parse::<usize>() {
|
||||
if komorebi_client::send_message(
|
||||
&SocketMessage::MonitorWorkAreaOffset(
|
||||
monitor_idx,
|
||||
rect,
|
||||
),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: MonitorWorkAreaOffset"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: Query"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(focused_window) = self.focused_window {
|
||||
if focused_window.enable {
|
||||
let titles = &komorebi_notification_state.focused_container_information.0;
|
||||
let icons = &komorebi_notification_state.focused_container_information.1;
|
||||
let focused_window_idx =
|
||||
komorebi_notification_state.focused_container_information.2;
|
||||
|
||||
let iter = titles.iter().zip(icons.iter());
|
||||
|
||||
for (i, (title, icon)) in iter.enumerate() {
|
||||
if focused_window.show_icon {
|
||||
if let Some(img) = icon {
|
||||
ui.add(
|
||||
Image::from(&img_to_texture(ctx, img))
|
||||
.maintain_aspect_ratio(true)
|
||||
.max_height(15.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if i == focused_window_idx {
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let layout_job = LayoutJob::simple(
|
||||
title.to_string(),
|
||||
font_id.clone(),
|
||||
komorebi_notification_state
|
||||
.stack_accent
|
||||
.unwrap_or(ctx.style().visuals.selection.stroke.color),
|
||||
100.0,
|
||||
);
|
||||
|
||||
if titles.len() > 1 {
|
||||
let available_height = ui.available_height();
|
||||
let mut custom_ui = CustomUi(ui);
|
||||
custom_ui.add_sized_left_to_right(
|
||||
Vec2::new(
|
||||
MAX_LABEL_WIDTH.load(Ordering::SeqCst) as f32,
|
||||
available_height,
|
||||
),
|
||||
Label::new(layout_job).selectable(false).truncate(),
|
||||
);
|
||||
} else {
|
||||
let available_height = ui.available_height();
|
||||
let mut custom_ui = CustomUi(ui);
|
||||
custom_ui.add_sized_left_to_right(
|
||||
Vec2::new(
|
||||
MAX_LABEL_WIDTH.load(Ordering::SeqCst) as f32,
|
||||
available_height,
|
||||
),
|
||||
Label::new(title).selectable(false).truncate(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let available_height = ui.available_height();
|
||||
let mut custom_ui = CustomUi(ui);
|
||||
|
||||
if custom_ui
|
||||
.add_sized_left_to_right(
|
||||
Vec2::new(
|
||||
MAX_LABEL_WIDTH.load(Ordering::SeqCst) as f32,
|
||||
available_height,
|
||||
),
|
||||
Label::new(title)
|
||||
.selectable(false)
|
||||
.sense(Sense::click())
|
||||
.truncate(),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
||||
false,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: MouseFollowsFocus"
|
||||
);
|
||||
}
|
||||
|
||||
if komorebi_client::send_message(&SocketMessage::FocusStackWindow(i))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: FocusStackWindow"
|
||||
);
|
||||
}
|
||||
|
||||
if komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
||||
komorebi_notification_state.mouse_follows_focus,
|
||||
))
|
||||
.is_err()
|
||||
{
|
||||
tracing::error!(
|
||||
"could not send message to komorebi: MouseFollowsFocus"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
||||
let size = [rgba_image.width() as usize, rgba_image.height() as usize];
|
||||
let pixels = rgba_image.as_flat_samples();
|
||||
let color_image = ColorImage::from_rgba_unmultiplied(size, pixels.as_slice());
|
||||
ctx.load_texture("icon", color_image, TextureOptions::default())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KomorebiNotificationState {
|
||||
pub workspaces: Vec<String>,
|
||||
pub selected_workspace: String,
|
||||
pub focused_container_information: (Vec<String>, Vec<Option<RgbaImage>>, usize),
|
||||
pub layout: KomorebiLayout,
|
||||
pub hide_empty_workspaces: bool,
|
||||
pub mouse_follows_focus: bool,
|
||||
pub work_area_offset: Option<Rect>,
|
||||
pub stack_accent: Option<Color32>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum KomorebiLayout {
|
||||
Default(komorebi_client::DefaultLayout),
|
||||
Floating,
|
||||
Paused,
|
||||
Custom,
|
||||
}
|
||||
|
||||
impl Display for KomorebiLayout {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
KomorebiLayout::Default(layout) => write!(f, "{layout}"),
|
||||
KomorebiLayout::Floating => write!(f, "Floating"),
|
||||
KomorebiLayout::Paused => write!(f, "Paused"),
|
||||
KomorebiLayout::Custom => write!(f, "Custom"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KomorebiNotificationState {
|
||||
pub fn update_from_config(&mut self, config: &Self) {
|
||||
self.hide_empty_workspaces = config.hide_empty_workspaces;
|
||||
}
|
||||
|
||||
pub fn handle_notification(
|
||||
&mut self,
|
||||
ctx: &Context,
|
||||
monitor_index: usize,
|
||||
rx_gui: Receiver<komorebi_client::Notification>,
|
||||
bg_color: Rc<RefCell<Color32>>,
|
||||
) {
|
||||
match rx_gui.try_recv() {
|
||||
Err(error) => match error {
|
||||
TryRecvError::Empty => {}
|
||||
TryRecvError::Disconnected => {
|
||||
tracing::error!(
|
||||
"failed to receive komorebi notification on gui thread: {error}"
|
||||
);
|
||||
}
|
||||
},
|
||||
Ok(notification) => {
|
||||
if let NotificationEvent::Socket(SocketMessage::ReloadStaticConfiguration(path)) =
|
||||
notification.event
|
||||
{
|
||||
if let Ok(config) = komorebi_client::StaticConfig::read(&path) {
|
||||
if let Some(theme) = config.theme {
|
||||
apply_theme(ctx, KomobarTheme::from(theme), bg_color);
|
||||
tracing::info!("applied theme from updated komorebi.json");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.mouse_follows_focus = notification.state.mouse_follows_focus;
|
||||
|
||||
let monitor = ¬ification.state.monitors.elements()[monitor_index];
|
||||
self.work_area_offset =
|
||||
notification.state.monitors.elements()[monitor_index].work_area_offset();
|
||||
|
||||
let focused_workspace_idx = monitor.focused_workspace_idx();
|
||||
|
||||
let mut workspaces = vec![];
|
||||
self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
|
||||
.name()
|
||||
.to_owned()
|
||||
.unwrap_or_else(|| format!("{}", focused_workspace_idx + 1));
|
||||
|
||||
for (i, ws) in monitor.workspaces().iter().enumerate() {
|
||||
let should_add = if self.hide_empty_workspaces {
|
||||
focused_workspace_idx == i || !ws.containers().is_empty()
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
if should_add {
|
||||
workspaces
|
||||
.push(ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
self.workspaces = workspaces;
|
||||
self.layout = match monitor.workspaces()[focused_workspace_idx].layout() {
|
||||
komorebi_client::Layout::Default(layout) => KomorebiLayout::Default(*layout),
|
||||
komorebi_client::Layout::Custom(_) => KomorebiLayout::Custom,
|
||||
};
|
||||
|
||||
if !*monitor.workspaces()[focused_workspace_idx].tile() {
|
||||
self.layout = KomorebiLayout::Floating;
|
||||
}
|
||||
|
||||
if notification.state.is_paused {
|
||||
self.layout = KomorebiLayout::Paused;
|
||||
}
|
||||
|
||||
if let Some(container) =
|
||||
monitor.workspaces()[focused_workspace_idx].monocle_container()
|
||||
{
|
||||
self.focused_container_information = (
|
||||
container
|
||||
.windows()
|
||||
.iter()
|
||||
.map(|w| w.title().unwrap_or_default())
|
||||
.collect::<Vec<_>>(),
|
||||
container
|
||||
.windows()
|
||||
.iter()
|
||||
.map(|w| windows_icons::get_icon_by_process_id(w.process_id()))
|
||||
.collect::<Vec<_>>(),
|
||||
container.focused_window_idx(),
|
||||
);
|
||||
} else if let Some(container) =
|
||||
monitor.workspaces()[focused_workspace_idx].focused_container()
|
||||
{
|
||||
self.focused_container_information = (
|
||||
container
|
||||
.windows()
|
||||
.iter()
|
||||
.map(|w| w.title().unwrap_or_default())
|
||||
.collect::<Vec<_>>(),
|
||||
container
|
||||
.windows()
|
||||
.iter()
|
||||
.map(|w| windows_icons::get_icon_by_process_id(w.process_id()))
|
||||
.collect::<Vec<_>>(),
|
||||
container.focused_window_idx(),
|
||||
);
|
||||
} else {
|
||||
self.focused_container_information.0.clear();
|
||||
self.focused_container_information.1.clear();
|
||||
self.focused_container_information.2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
411
komorebi-bar/src/main.rs
Normal file
411
komorebi-bar/src/main.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
mod bar;
|
||||
mod battery;
|
||||
mod config;
|
||||
mod cpu;
|
||||
mod date;
|
||||
mod komorebi;
|
||||
mod media;
|
||||
mod memory;
|
||||
mod network;
|
||||
mod storage;
|
||||
mod time;
|
||||
mod ui;
|
||||
mod widget;
|
||||
|
||||
use crate::bar::Komobar;
|
||||
use crate::config::KomobarConfig;
|
||||
use crate::config::Position;
|
||||
use crate::config::PositionConfig;
|
||||
use clap::Parser;
|
||||
use eframe::egui::ViewportBuilder;
|
||||
use font_loader::system_fonts;
|
||||
use hotwatch::EventKind;
|
||||
use hotwatch::Hotwatch;
|
||||
use komorebi_client::SocketMessage;
|
||||
use komorebi_client::SubscribeOptions;
|
||||
use schemars::gen::SchemaSettings;
|
||||
use std::io::BufReader;
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::AtomicI32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use windows::Win32::Foundation::BOOL;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
use windows::Win32::Foundation::LPARAM;
|
||||
use windows::Win32::System::Threading::GetCurrentProcessId;
|
||||
use windows::Win32::System::Threading::GetCurrentThreadId;
|
||||
use windows::Win32::UI::HiDpi::SetProcessDpiAwarenessContext;
|
||||
use windows::Win32::UI::HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2;
|
||||
use windows::Win32::UI::WindowsAndMessaging::EnumThreadWindows;
|
||||
use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId;
|
||||
|
||||
pub static WIDGET_SPACING: f32 = 10.0;
|
||||
|
||||
pub static MAX_LABEL_WIDTH: AtomicI32 = AtomicI32::new(400);
|
||||
pub static MONITOR_LEFT: AtomicI32 = AtomicI32::new(0);
|
||||
pub static MONITOR_TOP: AtomicI32 = AtomicI32::new(0);
|
||||
pub static MONITOR_RIGHT: AtomicI32 = AtomicI32::new(0);
|
||||
pub static BAR_HEIGHT: f32 = 50.0;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author, about, version)]
|
||||
struct Opts {
|
||||
/// Print the JSON schema of the configuration file and exit
|
||||
#[clap(long)]
|
||||
schema: bool,
|
||||
/// Print a list of fonts available on this system and exit
|
||||
#[clap(long)]
|
||||
fonts: bool,
|
||||
/// Path to a JSON or YAML configuration file
|
||||
#[clap(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
/// Write an example komorebi.bar.json to disk
|
||||
#[clap(long)]
|
||||
quickstart: bool,
|
||||
/// Print a list of aliases that can be renamed to canonical variants
|
||||
#[clap(long)]
|
||||
#[clap(hide = true)]
|
||||
aliases: bool,
|
||||
}
|
||||
|
||||
extern "system" fn enum_window(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
unsafe {
|
||||
let mut process_id = 0;
|
||||
GetWindowThreadProcessId(hwnd, Some(&mut process_id));
|
||||
|
||||
if process_id == GetCurrentProcessId() {
|
||||
*(lparam.0 as *mut HWND) = hwnd;
|
||||
BOOL::from(false) // Stop enumeration
|
||||
} else {
|
||||
BOOL::from(true) // Continue enumeration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn process_hwnd() -> Option<isize> {
|
||||
unsafe {
|
||||
let mut hwnd = HWND::default();
|
||||
let _ = EnumThreadWindows(
|
||||
GetCurrentThreadId(),
|
||||
Some(enum_window),
|
||||
LPARAM(&mut hwnd as *mut HWND as isize),
|
||||
);
|
||||
|
||||
if hwnd.0 as isize == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(hwnd.0 as isize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> color_eyre::Result<()> {
|
||||
unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }?;
|
||||
|
||||
let opts: Opts = Opts::parse();
|
||||
|
||||
if opts.schema {
|
||||
let settings = SchemaSettings::default().with(|s| {
|
||||
s.option_nullable = false;
|
||||
s.option_add_null_type = false;
|
||||
s.inline_subschemas = true;
|
||||
});
|
||||
|
||||
let gen = settings.into_generator();
|
||||
let socket_message = gen.into_root_schema_for::<KomobarConfig>();
|
||||
let schema = serde_json::to_string_pretty(&socket_message)?;
|
||||
|
||||
println!("{schema}");
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
if opts.fonts {
|
||||
for font in system_fonts::query_all() {
|
||||
println!("{font}");
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
if std::env::var("RUST_LIB_BACKTRACE").is_err() {
|
||||
std::env::set_var("RUST_LIB_BACKTRACE", "1");
|
||||
}
|
||||
|
||||
color_eyre::install()?;
|
||||
|
||||
if std::env::var("RUST_LOG").is_err() {
|
||||
std::env::set_var("RUST_LOG", "info");
|
||||
}
|
||||
|
||||
tracing::subscriber::set_global_default(
|
||||
tracing_subscriber::fmt::Subscriber::builder()
|
||||
.with_env_filter(EnvFilter::from_default_env())
|
||||
.finish(),
|
||||
)?;
|
||||
|
||||
let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else(
|
||||
|_| dirs::home_dir().expect("there is no home directory"),
|
||||
|home_path| {
|
||||
let home = PathBuf::from(&home_path);
|
||||
|
||||
if home.as_path().is_dir() {
|
||||
home
|
||||
} else {
|
||||
panic!("$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if opts.quickstart {
|
||||
let komorebi_bar_json = include_str!("../../docs/komorebi.bar.example.json").to_string();
|
||||
std::fs::write(home_dir.join("komorebi.bar.json"), komorebi_bar_json)?;
|
||||
println!(
|
||||
"Example komorebi.bar.json file written to {}",
|
||||
home_dir.as_path().display()
|
||||
);
|
||||
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let default_config_path = home_dir.join("komorebi.bar.json");
|
||||
|
||||
let config_path = opts.config.map_or_else(
|
||||
|| {
|
||||
if !default_config_path.is_file() {
|
||||
None
|
||||
} else {
|
||||
Some(default_config_path.clone())
|
||||
}
|
||||
},
|
||||
Option::from,
|
||||
);
|
||||
|
||||
let mut config = match config_path {
|
||||
None => {
|
||||
let komorebi_bar_json =
|
||||
include_str!("../../docs/komorebi.bar.example.json").to_string();
|
||||
|
||||
std::fs::write(&default_config_path, komorebi_bar_json)?;
|
||||
tracing::info!(
|
||||
"created example configuration file: {}",
|
||||
default_config_path.as_path().display()
|
||||
);
|
||||
|
||||
KomobarConfig::read(&default_config_path)?
|
||||
}
|
||||
Some(ref config) => {
|
||||
if !opts.aliases {
|
||||
tracing::info!(
|
||||
"found configuration file: {}",
|
||||
config.as_path().to_string_lossy()
|
||||
);
|
||||
}
|
||||
|
||||
KomobarConfig::read(config)?
|
||||
}
|
||||
};
|
||||
|
||||
let config_path = config_path.unwrap_or(default_config_path);
|
||||
|
||||
if opts.aliases {
|
||||
KomobarConfig::aliases(&std::fs::read_to_string(&config_path)?);
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
let state = serde_json::from_str::<komorebi_client::State>(&komorebi_client::send_query(
|
||||
&SocketMessage::State,
|
||||
)?)?;
|
||||
|
||||
MONITOR_RIGHT.store(
|
||||
state.monitors.elements()[config.monitor.index].size().right,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
MONITOR_TOP.store(
|
||||
state.monitors.elements()[config.monitor.index].size().top,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
MONITOR_TOP.store(
|
||||
state.monitors.elements()[config.monitor.index].size().left,
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
||||
match config.position {
|
||||
None => {
|
||||
config.position = Some(PositionConfig {
|
||||
start: Some(Position {
|
||||
x: state.monitors.elements()[config.monitor.index].size().left as f32,
|
||||
y: state.monitors.elements()[config.monitor.index].size().top as f32,
|
||||
}),
|
||||
end: Some(Position {
|
||||
x: state.monitors.elements()[config.monitor.index].size().right as f32,
|
||||
y: 50.0,
|
||||
}),
|
||||
})
|
||||
}
|
||||
Some(ref mut position) => {
|
||||
if position.start.is_none() {
|
||||
position.start = Some(Position {
|
||||
x: state.monitors.elements()[config.monitor.index].size().left as f32,
|
||||
y: state.monitors.elements()[config.monitor.index].size().top as f32,
|
||||
});
|
||||
}
|
||||
|
||||
if position.end.is_none() {
|
||||
position.end = Some(Position {
|
||||
x: state.monitors.elements()[config.monitor.index].size().right as f32,
|
||||
y: 50.0,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let viewport_builder = ViewportBuilder::default()
|
||||
.with_decorations(false)
|
||||
// .with_transparent(config.transparent)
|
||||
.with_taskbar(false);
|
||||
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: viewport_builder,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
if let Some(rect) = &config.monitor.work_area_offset {
|
||||
komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(
|
||||
config.monitor.index,
|
||||
*rect,
|
||||
))?;
|
||||
tracing::info!(
|
||||
"work area offset applied to monitor: {}",
|
||||
config.monitor.index
|
||||
);
|
||||
}
|
||||
|
||||
let (tx_gui, rx_gui) = crossbeam_channel::unbounded();
|
||||
let (tx_config, rx_config) = crossbeam_channel::unbounded();
|
||||
|
||||
let mut hotwatch = Hotwatch::new()?;
|
||||
let config_path_cl = config_path.clone();
|
||||
|
||||
hotwatch.watch(config_path, move |event| match event.kind {
|
||||
EventKind::Modify(_) | EventKind::Remove(_) => match KomobarConfig::read(&config_path_cl) {
|
||||
Ok(updated) => {
|
||||
tracing::info!(
|
||||
"configuration file updated: {}",
|
||||
config_path_cl.as_path().to_string_lossy()
|
||||
);
|
||||
|
||||
if let Err(error) = tx_config.send(updated) {
|
||||
tracing::error!("could not send configuration update to gui: {error}")
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{error}");
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
})?;
|
||||
|
||||
tracing::info!("watching configuration file for changes");
|
||||
|
||||
let config_arc = Arc::new(config);
|
||||
eframe::run_native(
|
||||
"komorebi-bar",
|
||||
native_options,
|
||||
Box::new(|cc| {
|
||||
let config_cl = config_arc.clone();
|
||||
|
||||
let ctx_repainter = cc.egui_ctx.clone();
|
||||
std::thread::spawn(move || loop {
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
ctx_repainter.request_repaint();
|
||||
});
|
||||
|
||||
let ctx_komorebi = cc.egui_ctx.clone();
|
||||
std::thread::spawn(move || {
|
||||
let subscriber_name = format!("komorebi-bar-{}", random_word::gen(random_word::Lang::En));
|
||||
|
||||
let listener = komorebi_client::subscribe_with_options(&subscriber_name, SubscribeOptions {
|
||||
filter_state_changes: true,
|
||||
})
|
||||
.expect("could not subscribe to komorebi notifications");
|
||||
|
||||
tracing::info!("subscribed to komorebi notifications: \"{}\"", subscriber_name);
|
||||
|
||||
for client in listener.incoming() {
|
||||
match client {
|
||||
Ok(subscription) => {
|
||||
let mut buffer = Vec::new();
|
||||
let mut reader = BufReader::new(subscription);
|
||||
|
||||
// this is when we know a shutdown has been sent
|
||||
if matches!(reader.read_to_end(&mut buffer), Ok(0)) {
|
||||
tracing::info!("disconnected from komorebi");
|
||||
|
||||
// keep trying to reconnect to komorebi
|
||||
while komorebi_client::send_message(
|
||||
&SocketMessage::AddSubscriberSocket(subscriber_name.clone()),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
|
||||
tracing::info!("reconnected to komorebi");
|
||||
|
||||
if let Some(rect) = &config_cl.monitor.work_area_offset {
|
||||
while komorebi_client::send_message(
|
||||
&SocketMessage::MonitorWorkAreaOffset(
|
||||
config_cl.monitor.index,
|
||||
*rect,
|
||||
),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
std::thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match String::from_utf8(buffer) {
|
||||
Ok(notification_string) => {
|
||||
match serde_json::from_str::<komorebi_client::Notification>(
|
||||
¬ification_string,
|
||||
) {
|
||||
Ok(notification) => {
|
||||
tracing::debug!("received notification from komorebi");
|
||||
|
||||
if let Err(error) = tx_gui.send(notification) {
|
||||
tracing::error!("could not send komorebi notification update to gui thread: {error}")
|
||||
}
|
||||
|
||||
ctx_komorebi.request_repaint();
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("could not deserialize komorebi notification: {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!(
|
||||
"komorebi notification string was invalid utf8: {error}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Box::new(Komobar::new(cc, rx_gui, rx_config, config_arc)))
|
||||
}),
|
||||
)
|
||||
.map_err(|error| color_eyre::eyre::Error::msg(error.to_string()))
|
||||
}
|
||||
128
komorebi-bar/src/media.rs
Normal file
128
komorebi-bar/src/media.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::ui::CustomUi;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::MAX_LABEL_WIDTH;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use eframe::egui::Vec2;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use windows::Media::Control::GlobalSystemMediaTransportControlsSessionManager;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MediaConfig {
|
||||
/// Enable the Media widget
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
impl From<MediaConfig> for Media {
|
||||
fn from(value: MediaConfig) -> Self {
|
||||
Self::new(value.enable)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Media {
|
||||
pub enable: bool,
|
||||
pub session_manager: GlobalSystemMediaTransportControlsSessionManager,
|
||||
}
|
||||
|
||||
impl Media {
|
||||
pub fn new(enable: bool) -> Self {
|
||||
Self {
|
||||
enable,
|
||||
session_manager: GlobalSystemMediaTransportControlsSessionManager::RequestAsync()
|
||||
.unwrap()
|
||||
.get()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle(&self) {
|
||||
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
||||
if let Ok(op) = session.TryTogglePlayPauseAsync() {
|
||||
op.get().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn output(&mut self) -> String {
|
||||
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
||||
if let Ok(operation) = session.TryGetMediaPropertiesAsync() {
|
||||
if let Ok(properties) = operation.get() {
|
||||
if let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title()) {
|
||||
if artist.is_empty() {
|
||||
return format!("{title}");
|
||||
}
|
||||
|
||||
if title.is_empty() {
|
||||
return format!("{artist}");
|
||||
}
|
||||
|
||||
return format!("{artist} - {title}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Media {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.is_empty() {
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
egui_phosphor::regular::HEADPHONES.to_string(),
|
||||
font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
|
||||
);
|
||||
|
||||
let available_height = ui.available_height();
|
||||
let mut custom_ui = CustomUi(ui);
|
||||
|
||||
if custom_ui
|
||||
.add_sized_left_to_right(
|
||||
Vec2::new(
|
||||
MAX_LABEL_WIDTH.load(Ordering::SeqCst) as f32,
|
||||
available_height,
|
||||
),
|
||||
Label::new(layout_job)
|
||||
.selectable(false)
|
||||
.sense(Sense::click())
|
||||
.truncate(),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
self.toggle();
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
komorebi-bar/src/memory.rs
Normal file
123
komorebi-bar/src/memory.rs
Normal file
@@ -0,0 +1,123 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::widget::BarWidget;
|
||||
use crate::WIDGET_SPACING;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::FontId;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::TextStyle;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::RefreshKind;
|
||||
use sysinfo::System;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MemoryConfig {
|
||||
/// Enable the Memory widget
|
||||
pub enable: bool,
|
||||
/// Data refresh interval (default: 10 seconds)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
}
|
||||
|
||||
impl From<MemoryConfig> for Memory {
|
||||
fn from(value: MemoryConfig) -> Self {
|
||||
let mut system =
|
||||
System::new_with_specifics(RefreshKind::default().without_cpu().without_processes());
|
||||
|
||||
system.refresh_memory();
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
system,
|
||||
data_refresh_interval: value.data_refresh_interval.unwrap_or(10),
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Memory {
|
||||
pub enable: bool,
|
||||
system: System,
|
||||
data_refresh_interval: u64,
|
||||
label_prefix: LabelPrefix,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
fn output(&mut self) -> String {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.system.refresh_memory();
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
let used = self.system.used_memory();
|
||||
let total = self.system.total_memory();
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Text | LabelPrefix::IconAndText => {
|
||||
format!("RAM: {}%", (used * 100) / total)
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{}%", (used * 100) / total),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Memory {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.is_empty() {
|
||||
let font_id = ctx
|
||||
.style()
|
||||
.text_styles
|
||||
.get(&TextStyle::Body)
|
||||
.cloned()
|
||||
.unwrap_or_else(FontId::default);
|
||||
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
egui_phosphor::regular::MEMORY.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat::simple(font_id, ctx.style().visuals.text_color()),
|
||||
);
|
||||
|
||||
if ui
|
||||
.add(
|
||||
Label::new(layout_job)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("cmd.exe").args(["/C", "taskmgr.exe"]).spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui.add_space(WIDGET_SPACING);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user