mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-14 22:13:13 +01:00
Compare commits
20 Commits
feature/ic
...
v0.1.33
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc51f62c3a | ||
|
|
b1db417df5 | ||
|
|
996a556984 | ||
|
|
c71e61fb1e | ||
|
|
2d97ee101d | ||
|
|
a4f69238b7 | ||
|
|
96f7eb1d31 | ||
|
|
28cd4a8801 | ||
|
|
3aa92a1255 | ||
|
|
281980b010 | ||
|
|
c063302c91 | ||
|
|
ba52dc3378 | ||
|
|
44716fdc98 | ||
|
|
4b30cecba9 | ||
|
|
d45cd729e8 | ||
|
|
5a8f48c6b9 | ||
|
|
4b9d811499 | ||
|
|
d520a2bf74 | ||
|
|
7ef4fd81c0 | ||
|
|
083ab65077 |
429
Cargo.lock
generated
429
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -31,13 +31,13 @@ tracing = "0.1"
|
||||
tracing-appender = "0.2"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
paste = "1"
|
||||
sysinfo = "0.31"
|
||||
sysinfo = "0.33"
|
||||
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"
|
||||
shadow-rs = "0.37"
|
||||
which = "7"
|
||||
|
||||
[workspace.dependencies.windows]
|
||||
|
||||
115
README.md
115
README.md
@@ -29,6 +29,8 @@ Tiling Window Management for Windows.
|
||||
|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
_komorebi_ is a tiling window manager that works as an extension to Microsoft's
|
||||
[Desktop Window
|
||||
Manager](https://docs.microsoft.com/en-us/windows/win32/dwm/dwm-overview) in
|
||||
@@ -50,6 +52,8 @@ _komorebi_, [common workflows](https://lgug2z.github.io/komorebi/common-workflow
|
||||
[configuration schema reference](https://komorebi.lgug2z.com/schema) and a
|
||||
complete [CLI reference](https://lgug2z.github.io/komorebi/cli/quickstart.html).
|
||||
|
||||
## Community
|
||||
|
||||
There is a [Discord server](https://discord.gg/mGkn66PHkx) available for
|
||||
_komorebi_-related discussion, help, troubleshooting etc. If you have any
|
||||
specific feature requests or bugs to report, please create an issue in this
|
||||
@@ -57,28 +61,62 @@ repository.
|
||||
|
||||
There is a [YouTube
|
||||
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_ development videos, feature previews and release overviews. Subscribing
|
||||
to the channel (which is monetized as part of the YouTube Partner Program) and
|
||||
watching videos is a really simple and passive way to contribute financially to
|
||||
the development and maintenance of _komorebi_.
|
||||
|
||||
There is an [Awesome List](https://github.com/LGUG2Z/awesome-komorebi) which
|
||||
showcases the many awesome projects that exist in the _komorebi_ ecosystem.
|
||||
|
||||
_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
|
||||
## Licensing for Personal Use
|
||||
|
||||
`komorebi` is licensed under the [Komorebi 1.0.0
|
||||
license](https://github.com/LGUG2Z/komorebi-license), 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` for
|
||||
personal use other than redistribution, or distribution of new works (i.e.
|
||||
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.
|
||||
|
||||
The [Komorebi 1.0.0 License](https://github.com/LGUG2Z/komorebi-license) does
|
||||
not permit any kind of commercial use (i.e. using `komorebi` at work).
|
||||
|
||||
## Sponsorship for Personal Use
|
||||
|
||||
_komorebi_ is a free and educational source project, and one that encourages you
|
||||
to make charitable donations if you find the software to be useful and have the
|
||||
financial means.
|
||||
|
||||
I encourage you to make a charitable donation to the [Palestine Children's
|
||||
Relief Fund](https://pcrf1.app.neoncrm.com/forms/gaza-recovery) or contributing
|
||||
Relief Fund](https://pcrf1.app.neoncrm.com/forms/gaza-recovery) or to contribute
|
||||
to a [Gaza Funds campaign](https://gazafunds.com) before you consider sponsoring
|
||||
me on GitHub.
|
||||
|
||||
[GitHub Sponsors is enabled for this
|
||||
project](https://github.com/sponsors/LGUG2Z). Unfortunately I don't have
|
||||
anything specific to offer besides my gratitude and shout outs at the end of
|
||||
_komorebi_ live development videos and tutorials.
|
||||
project](https://github.com/sponsors/LGUG2Z). Sponsors can claim custom roles on
|
||||
the Discord server, get shout outs at the end of _komorebi_-related videos on
|
||||
YouTube, and gain the ability to submit feature requests on the issue tracker.
|
||||
|
||||
If you would like to tip or sponsor the project but are unable to use GitHub
|
||||
Sponsors, you may also sponsor through [Ko-fi](https://ko-fi.com/lgug2z).
|
||||
Sponsors, you may also sponsor through [Ko-fi](https://ko-fi.com/lgug2z), or
|
||||
make an anonymous Bitcoin donation to `bc1qv73wzspc77k46uty4vp85x8sdp24mphvm58f6q`.
|
||||
|
||||
## Licensing for Commercial Use
|
||||
|
||||
A dedicated Individual Commercial Use License is available for those who want to
|
||||
use `komorebi` at work.
|
||||
|
||||
The Individual Commerical Use License adds “Commercial Use” as a “Permitted Use”
|
||||
for the licensed individual only, for the duration of a valid paid license
|
||||
subscription only. All provisions and restrictions enumerated in the [Komorebi
|
||||
License](https://github.com/LGUG2Z/komorebi-license) continue to apply.
|
||||
|
||||
More information, pricing and purchase links for Individual Commercial Use
|
||||
Licenses [can be found here](https://lgug2z.com/software/komorebi).
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -125,7 +163,11 @@ https://user-images.githubusercontent.com/13164844/163496414-a9cde3d1-b8a7-4a7a-
|
||||
|
||||
# Contribution Guidelines
|
||||
|
||||
If you would like to contribute to `komorebi` please take the time to carefully read the guidelines below.
|
||||
If you would like to contribute to `komorebi` please take the time to carefully
|
||||
read the guidelines below.
|
||||
|
||||
Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for more information about how
|
||||
code contributions to `komorebi` are licensed.
|
||||
|
||||
## Commit hygiene
|
||||
|
||||
@@ -135,8 +177,8 @@ If you would like to contribute to `komorebi` please take the time to carefully
|
||||
- Use `git cz` with
|
||||
the [Commitizen CLI](https://github.com/commitizen/cz-cli#conventional-commit-messages-as-a-global-utility) to prepare
|
||||
commit messages
|
||||
- Provide **at least** one short sentence or paragraph in your commit message body to describe your thought process for the
|
||||
changes being committed
|
||||
- Provide **at least** one short sentence or paragraph in your commit message body to describe your thought process for
|
||||
the changes being committed
|
||||
|
||||
## PRs should contain only a single feature or bug fix
|
||||
|
||||
@@ -175,7 +217,8 @@ This includes but is not limited to:
|
||||
|
||||
- All `komorebic` commands
|
||||
- The `komorebi.json` schema
|
||||
- The [`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
|
||||
- The [
|
||||
`komorebi-application-specific-configuration`](https://github.com/LGUG2Z/komorebi-application-specific-configuration)
|
||||
schema
|
||||
|
||||
No user should ever find that their configuration file has stopped working after upgrading to a new version
|
||||
@@ -191,27 +234,6 @@ 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` for
|
||||
personal use 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.
|
||||
|
||||
The [Komorebi 1.0.0 License](./LICENSE.md) does not permit any kind of
|
||||
commercial use.
|
||||
|
||||
A dedicated license and EULA will be introduced in 2025 for both commercial and
|
||||
noncommercial organizations.
|
||||
|
||||
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
|
||||
@@ -220,13 +242,13 @@ the IDE for completions and navigation:
|
||||
- Set `Expand declarative macros`
|
||||
to `Use new engine` under "Settings > Langauges & Frameworks > Rust"
|
||||
- Enable the following experimental features:
|
||||
- `org.rust.cargo.evaluate.build.scripts`
|
||||
- `org.rust.macros.proc`
|
||||
- `org.rust.cargo.evaluate.build.scripts`
|
||||
- `org.rust.macros.proc`
|
||||
|
||||
# Logs and Debugging
|
||||
|
||||
Logs from `komorebi` will be appended to `%LOCALAPPDATA%/komorebi/komorebi.log`; this file is never rotated or overwritten, so it will keep
|
||||
growing until it is deleted by the user.
|
||||
Logs from `komorebi` will be appended to `%LOCALAPPDATA%/komorebi/komorebi.log`; this file is never rotated or
|
||||
overwritten, so it will keep growing until it is deleted by the user.
|
||||
|
||||
Whenever running the `komorebic stop` command or sending a Ctrl-C signal to `komorebi` directly, the `komorebi` process
|
||||
ensures that all hidden windows are restored before termination.
|
||||
@@ -367,7 +389,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.31"}
|
||||
// komorebi-client = { git = "https://github.com/LGUG2Z/komorebi", tag = "v0.1.33"}
|
||||
|
||||
use anyhow::Result;
|
||||
use komorebi_client::Notification;
|
||||
@@ -442,12 +464,17 @@ programming languages.
|
||||
|
||||
# Appreciations
|
||||
|
||||
- First and foremost, thank you to my wife, both for naming this project and for her patience throughout its never-ending development
|
||||
- First and foremost, thank you to my wife, both for naming this project and for her patience throughout its
|
||||
never-ending development
|
||||
|
||||
- Thank you to [@sitiom](https://github.com/sitiom) for being [an exemplary open source community leader](https://jeezy.substack.com/p/the-open-source-contributions-i-appreciate)
|
||||
- Thank you to [@sitiom](https://github.com/sitiom) for
|
||||
being [an exemplary open source community leader](https://jeezy.substack.com/p/the-open-source-contributions-i-appreciate)
|
||||
|
||||
- Thank you to the developers of [nog](https://github.com/TimUntersberger/nog) who came before me and whose work taught me more than I can ever hope to repay
|
||||
- Thank you to the developers of [nog](https://github.com/TimUntersberger/nog) who came before me and whose work taught
|
||||
me more than I can ever hope to repay
|
||||
|
||||
- Thank you to the developers of [GlazeWM](https://github.com/lars-berger/GlazeWM) for pushing the boundaries of tiling window management on Windows with me and having an excellent spirit of collaboration
|
||||
- Thank you to the developers of [GlazeWM](https://github.com/lars-berger/GlazeWM) for pushing the boundaries of tiling
|
||||
window management on Windows with me and having an excellent spirit of collaboration
|
||||
|
||||
- Thank you to [@Ciantic](https://github.com/Ciantic) for helping me bring the [hidden Virtual Desktops cloaking function](https://github.com/Ciantic/AltTabAccessor/issues/1) to `komorebi`
|
||||
- Thank you to [@Ciantic](https://github.com/Ciantic) for helping me bring
|
||||
the [hidden Virtual Desktops cloaking function](https://github.com/Ciantic/AltTabAccessor/issues/1) to `komorebi`
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
```
|
||||
Set the duration for movement animations in ms
|
||||
|
||||
Usage: komorebic.exe animation-duration <DURATION>
|
||||
Usage: komorebic.exe animation-duration [OPTIONS] <DURATION>
|
||||
|
||||
Arguments:
|
||||
<DURATION>
|
||||
Desired animation durations in ms
|
||||
|
||||
Options:
|
||||
-a, --animation-type <ANIMATION_TYPE>
|
||||
Animation type to apply the duration to. If not specified, sets global duration
|
||||
|
||||
[possible values: movement, transparency]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -10,8 +10,14 @@ Options:
|
||||
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]
|
||||
[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]
|
||||
|
||||
-a, --animation-type <ANIMATION_TYPE>
|
||||
Animation type to apply the style to. If not specified, sets global style
|
||||
|
||||
[possible values: movement, transparency]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
```
|
||||
Enable or disable movement animations
|
||||
|
||||
Usage: komorebic.exe animation <BOOLEAN_STATE>
|
||||
Usage: komorebic.exe animation [OPTIONS] <BOOLEAN_STATE>
|
||||
|
||||
Arguments:
|
||||
<BOOLEAN_STATE>
|
||||
[possible values: enable, disable]
|
||||
|
||||
Options:
|
||||
-a, --animation-type <ANIMATION_TYPE>
|
||||
Animation type to apply the state to. If not specified, sets global state
|
||||
|
||||
[possible values: movement, transparency]
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
```
|
||||
Check komorebi configuration and related files for common errors
|
||||
|
||||
Usage: komorebic.exe check
|
||||
Usage: komorebic.exe check [OPTIONS]
|
||||
|
||||
Options:
|
||||
-k, --komorebi-config <KOMOREBI_CONFIG>
|
||||
Path to a static configuration JSON file
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
12
docs/cli/close-workspace.md
Normal file
12
docs/cli/close-workspace.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# close-workspace
|
||||
|
||||
```
|
||||
Close the focused workspace (must be empty and unnamed)
|
||||
|
||||
Usage: komorebic.exe close-workspace
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/cycle-stack-index.md
Normal file
16
docs/cli/cycle-stack-index.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# cycle-stack-index
|
||||
|
||||
```
|
||||
Cycle the index of the focused window in the focused stack in the specified cycle direction
|
||||
|
||||
Usage: komorebic.exe cycle-stack-index <CYCLE_DIRECTION>
|
||||
|
||||
Arguments:
|
||||
<CYCLE_DIRECTION>
|
||||
[possible values: previous, next]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
16
docs/cli/eager-focus.md
Normal file
16
docs/cli/eager-focus.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# eager-focus
|
||||
|
||||
```
|
||||
Focus the first managed window matching the given exe
|
||||
|
||||
Usage: komorebic.exe eager-focus <EXE>
|
||||
|
||||
Arguments:
|
||||
<EXE>
|
||||
Case-sensitive exe identifier
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -18,6 +18,9 @@ Options:
|
||||
--bar
|
||||
Enable autostart of komorebi-bar
|
||||
|
||||
--masir
|
||||
Enable autostart of masir
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
12
docs/cli/enforce-workspace-rules.md
Normal file
12
docs/cli/enforce-workspace-rules.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# enforce-workspace-rules
|
||||
|
||||
```
|
||||
Enforce all workspace rules, including initial workspace rules that have already been applied
|
||||
|
||||
Usage: komorebic.exe enforce-workspace-rules
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
24
docs/cli/kill.md
Normal file
24
docs/cli/kill.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# kill
|
||||
|
||||
```
|
||||
Kill background processes started by komorebic
|
||||
|
||||
Usage: komorebic.exe kill [OPTIONS]
|
||||
|
||||
Options:
|
||||
--whkd
|
||||
Kill whkd if it is running as a background process
|
||||
|
||||
--ahk
|
||||
Kill ahk if it is running as a background process
|
||||
|
||||
--bar
|
||||
Kill komorebi-bar if it is running as a background process
|
||||
|
||||
--masir
|
||||
Kill masir if it is running as a background process
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
18
docs/cli/stackbar-mode.md
Normal file
18
docs/cli/stackbar-mode.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# stackbar-mode
|
||||
|
||||
```
|
||||
Set the stackbar mode
|
||||
|
||||
Usage: komorebic.exe stackbar-mode <MODE>
|
||||
|
||||
Arguments:
|
||||
<MODE>
|
||||
Desired stackbar mode
|
||||
|
||||
[possible values: always, never, on-stack]
|
||||
|
||||
Options:
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
```
|
||||
@@ -24,6 +24,12 @@ Options:
|
||||
--bar
|
||||
Start komorebi-bar in a background process
|
||||
|
||||
--masir
|
||||
Start masir in a background process for focus-follows-mouse
|
||||
|
||||
--clean-state
|
||||
Do not attempt to auto-apply a dumped state temp file from a previously running instance of komorebi
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ Options:
|
||||
--bar
|
||||
Stop komorebi-bar if it is running as a background process
|
||||
|
||||
--masir
|
||||
Stop masir if it is running as a background process
|
||||
|
||||
-h, --help
|
||||
Print help
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# toggle-workspace-float-override
|
||||
|
||||
```
|
||||
Enable or disable float override, which makes it so every new window opens in floating mode, for the currently focused workspace. If there was no override value set for the workspace previously it takes the opposite of the global value
|
||||
Enable or disable float override, which makes it so every new window opens in floating mode, for the currently focused workspace. If there was no override value set for the workspace
|
||||
previously it takes the opposite of the global value
|
||||
|
||||
Usage: komorebic.exe toggle-workspace-float-override
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# toggle-workspace-window-container-behaviour
|
||||
|
||||
```
|
||||
Toggle the behaviour for new windows (stacking or dynamic tiling) for currently focused workspace. If there was no behaviour set for the workspace previously it takes the opposite of the global value
|
||||
Toggle the behaviour for new windows (stacking or dynamic tiling) for currently focused workspace. If there was no behaviour set for the workspace previously it takes the opposite of the
|
||||
global value
|
||||
|
||||
Usage: komorebic.exe toggle-workspace-window-container-behaviour
|
||||
|
||||
|
||||
@@ -181,10 +181,10 @@ The `grid` layout does not support resizing windows tiles.
|
||||
key bindings go to the left of the colon, and shell commands go to the right of the
|
||||
colon.
|
||||
|
||||
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,
|
||||
I recommend using [AutoHotKey](https://autohotkey.com) to set up your key
|
||||
bindings for `komorebic` commands instead.
|
||||
As of [`v0.2.4`](https://github.com/LGUG2Z/whkd/releases/tag/v0.2.4), `whkd` can override most of Microsoft's
|
||||
limitations on hotkey bindings that include the `win` key. However, you will still need
|
||||
to [modify the registry](https://superuser.com/questions/1059511/how-to-disable-winl-in-windows-10) to prevent
|
||||
`win + l` from locking the operating system.
|
||||
|
||||
```
|
||||
{% include "./whkdrc.sample" %}
|
||||
@@ -203,7 +203,7 @@ It is also possible to change a hotkey behavior depending on which application h
|
||||
alt + n [
|
||||
# ProcessName as shown by `Get-Process`
|
||||
Firefox : echo "hello firefox"
|
||||
|
||||
|
||||
# Spaces are fine, no quotes required
|
||||
Google Chrome : echo "hello chrome"
|
||||
]
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||

|
||||
|
||||
## Overview
|
||||
|
||||
`komorebi` is a tiling window manager that works as an extension to Microsoft's
|
||||
[Desktop Window
|
||||
Manager](https://docs.microsoft.com/en-us/windows/win32/dwm/dwm-overview) in
|
||||
@@ -15,12 +17,63 @@ system and desktop environment by default. Users are free to make such
|
||||
modifications in their own configuration files for `komorebi`, but these will
|
||||
always remain opt-in and off-by-default.
|
||||
|
||||
## Community
|
||||
|
||||
There is a [Discord server](https://discord.gg/mGkn66PHkx) available for
|
||||
`komorebi`-related discussion, help, troubleshooting etc. If you have any
|
||||
specific feature requests or bugs to report, please create an issue on
|
||||
[GitHub](https://github.com/LGUG2Z/komorebi).
|
||||
`komorebi`-related discussion, help, troubleshooting etc.
|
||||
|
||||
There is a [YouTube
|
||||
channel](https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg) where I post
|
||||
`komorebi` development videos, feature previews and release overviews. Subscribing
|
||||
to the channel (which is monetized as part of the YouTube Partner Program) and
|
||||
watching videos is a really simple and passive way to contribute financially to
|
||||
the development and maintenance of `komorebi`.
|
||||
|
||||
There is also a [YouTube
|
||||
channel](https://www.youtube.com/channel/UCeai3-do-9O4MNy9_xjO6mg?sub_confirmation=1)
|
||||
where I share `komorebi` live programming videos and tutorial videos.
|
||||
There is an [Awesome List](https://github.com/LGUG2Z/awesome-komorebi) which
|
||||
showcases the many awesome projects that exist in the `komorebi` ecosystem.
|
||||
|
||||
## Licensing for Personal Use
|
||||
|
||||
`komorebi` is licensed under the [Komorebi 1.0.0 license](https://github.com/LGUG2Z/komorebi-license), 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` for personal use other than redistribution, or distribution of
|
||||
new works (i.e. 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.
|
||||
|
||||
The [Komorebi 1.0.0 License](https://github.com/LGUG2Z/komorebi-license) does not permit any kind of commercial use (
|
||||
i.e. using `komorebi` at work).
|
||||
|
||||
## Sponsorship for Personal Use
|
||||
|
||||
`komorebi` is a free and educational source project, and one that encourages you
|
||||
to make charitable donations if you find the software to be useful and have the
|
||||
financial means.
|
||||
|
||||
I encourage you to make a charitable donation to the [Palestine Children's
|
||||
Relief Fund](https://pcrf1.app.neoncrm.com/forms/gaza-recovery) or to contribute
|
||||
to a [Gaza Funds campaign](https://gazafunds.com) before you consider sponsoring
|
||||
me on GitHub.
|
||||
|
||||
[GitHub Sponsors is enabled for this
|
||||
project](https://github.com/sponsors/LGUG2Z). Sponsors can claim custom roles on
|
||||
the Discord server, get shout-outs at the end of _komorebi_-related videos on
|
||||
YouTube, and gain the ability to submit feature requests on the issue tracker.
|
||||
|
||||
If you would like to tip or sponsor the project but are unable to use GitHub
|
||||
Sponsors, you may also sponsor through [Ko-fi](https://ko-fi.com/lgug2z), or
|
||||
make an anonymous Bitcoin donation to `bc1qv73wzspc77k46uty4vp85x8sdp24mphvm58f6q`.
|
||||
|
||||
## Licensing for Commercial Use
|
||||
|
||||
A dedicated Individual Commercial Use License is available for those who want to
|
||||
use `komorebi` at work.
|
||||
|
||||
The Individual Commerical Use License adds “Commercial Use” as a “Permitted Use”
|
||||
for the licensed individual only, for the duration of a valid paid license
|
||||
subscription only. All provisions and restrictions enumerated in the [Komorebi
|
||||
License](https://github.com/LGUG2Z/komorebi-license) continue to apply.
|
||||
|
||||
More information, pricing and purchase links for Individual Commercial Use
|
||||
Licenses [can be found here](https://lgug2z.com/software/komorebi).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.31/schema.bar.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.33/schema.bar.json",
|
||||
"monitor": {
|
||||
"index": 0,
|
||||
"work_area_offset": {
|
||||
@@ -33,6 +33,11 @@
|
||||
}
|
||||
],
|
||||
"right_widgets": [
|
||||
{
|
||||
"Update": {
|
||||
"enable": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Media": {
|
||||
"enable": true
|
||||
@@ -73,4 +78,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.31/schema.json",
|
||||
"$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.33/schema.json",
|
||||
"app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
|
||||
"window_hiding_behaviour": "Cloak",
|
||||
"cross_monitor_move_behaviour": "Insert",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-bar"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -20,11 +20,12 @@ egui-phosphor = "0.8"
|
||||
font-loader = "0.11"
|
||||
hotwatch = { workspace = true }
|
||||
image = "0.25"
|
||||
netdev = "0.31"
|
||||
netdev = "0.32"
|
||||
num = "0.4"
|
||||
num-derive = "0.4"
|
||||
num-traits = "0.2"
|
||||
random_word = { version = "0.4", features = ["en"] }
|
||||
reqwest = { version = "0.12", features = ["blocking"] }
|
||||
schemars = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
||||
@@ -50,6 +50,7 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Komobar {
|
||||
pub hwnd: Option<isize>,
|
||||
pub config: Arc<KomobarConfig>,
|
||||
pub render_config: Rc<RefCell<RenderConfig>>,
|
||||
pub komorebi_notification_state: Option<Rc<RefCell<KomorebiNotificationState>>>,
|
||||
@@ -61,6 +62,7 @@ pub struct Komobar {
|
||||
pub bg_color: Rc<RefCell<Color32>>,
|
||||
pub bg_color_with_alpha: Rc<RefCell<Color32>>,
|
||||
pub scale_factor: f32,
|
||||
pub size_rect: komorebi_client::Rect,
|
||||
applied_theme_on_first_frame: bool,
|
||||
}
|
||||
|
||||
@@ -194,49 +196,9 @@ impl Komobar {
|
||||
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,
|
||||
});
|
||||
|
||||
if end.y == 0.0 {
|
||||
tracing::warn!("position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default")
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the `size_rect` so that the bar position can be changed on the EGUI update
|
||||
// function
|
||||
self.update_size_rect(config.position.clone());
|
||||
|
||||
self.try_apply_theme(config, ctx);
|
||||
|
||||
@@ -307,7 +269,7 @@ impl Komobar {
|
||||
Some(widget.komorebi_notification_state.clone());
|
||||
}
|
||||
Some(ref previous) => {
|
||||
if widget.workspaces.map_or(false, |w| w.enable) {
|
||||
if widget.workspaces.is_some_and(|w| w.enable) {
|
||||
previous.borrow_mut().update_from_config(
|
||||
&widget.komorebi_notification_state.borrow(),
|
||||
);
|
||||
@@ -361,6 +323,41 @@ impl Komobar {
|
||||
self.config = config.clone().into();
|
||||
}
|
||||
|
||||
/// Updates the `size_rect` field. Returns a bool indicating if the field was changed or not
|
||||
fn update_size_rect(&mut self, position: Option<PositionConfig>) {
|
||||
let position = position.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,
|
||||
}),
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
if end.y == 0.0 {
|
||||
tracing::warn!("position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default")
|
||||
}
|
||||
|
||||
self.size_rect = komorebi_client::Rect {
|
||||
left: start.x as i32,
|
||||
top: start.y as i32,
|
||||
right: end.x as i32,
|
||||
bottom: end.y as i32,
|
||||
};
|
||||
}
|
||||
|
||||
fn try_apply_theme(&mut self, config: &KomobarConfig, ctx: &Context) {
|
||||
match config.theme {
|
||||
Some(theme) => {
|
||||
@@ -454,6 +451,7 @@ impl Komobar {
|
||||
config: Arc<KomobarConfig>,
|
||||
) -> Self {
|
||||
let mut komobar = Self {
|
||||
hwnd: process_hwnd(),
|
||||
config: config.clone(),
|
||||
render_config: Rc::new(RefCell::new(RenderConfig::new())),
|
||||
komorebi_notification_state: None,
|
||||
@@ -465,6 +463,7 @@ impl Komobar {
|
||||
bg_color: Rc::new(RefCell::new(Style::default().visuals.panel_fill)),
|
||||
bg_color_with_alpha: Rc::new(RefCell::new(Style::default().visuals.panel_fill)),
|
||||
scale_factor: cc.egui_ctx.native_pixels_per_point().unwrap_or(1.0),
|
||||
size_rect: komorebi_client::Rect::default(),
|
||||
applied_theme_on_first_frame: false,
|
||||
};
|
||||
|
||||
@@ -535,6 +534,10 @@ impl eframe::App for Komobar {
|
||||
}
|
||||
|
||||
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||
if self.hwnd.is_none() {
|
||||
self.hwnd = process_hwnd();
|
||||
}
|
||||
|
||||
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(
|
||||
@@ -573,6 +576,31 @@ impl eframe::App for Komobar {
|
||||
self.applied_theme_on_first_frame = true;
|
||||
}
|
||||
|
||||
// Check if egui's Window size is the expected one, if not, update it
|
||||
if let Some(current_rect) = ctx.input(|i| i.viewport().outer_rect) {
|
||||
// Get the correct size according to scale factor
|
||||
let current_rect = komorebi_client::Rect {
|
||||
left: (current_rect.min.x * self.scale_factor) as i32,
|
||||
top: (current_rect.min.y * self.scale_factor) as i32,
|
||||
right: ((current_rect.max.x - current_rect.min.x) * self.scale_factor) as i32,
|
||||
bottom: ((current_rect.max.y - current_rect.min.y) * self.scale_factor) as i32,
|
||||
};
|
||||
|
||||
if self.size_rect != current_rect {
|
||||
if let Some(hwnd) = self.hwnd {
|
||||
let window = komorebi_client::Window::from(hwnd);
|
||||
match window.set_position(&self.size_rect, false) {
|
||||
Ok(_) => {
|
||||
tracing::info!("updated bar position");
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::error!("{}", error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let frame = if let Some(frame) = &self.config.frame {
|
||||
Frame::none()
|
||||
.inner_margin(Margin::symmetric(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::render::RenderConfig;
|
||||
use crate::selected_frame::SelectableFrame;
|
||||
use crate::widget::BarWidget;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::Sense;
|
||||
use eframe::egui::TextFormat;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
@@ -14,6 +14,7 @@ use serde::Serialize;
|
||||
use starship_battery::units::ratio::percent;
|
||||
use starship_battery::Manager;
|
||||
use starship_battery::State;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
@@ -21,6 +22,8 @@ use std::time::Instant;
|
||||
pub struct BatteryConfig {
|
||||
/// Enable the Battery widget
|
||||
pub enable: bool,
|
||||
/// Hide the widget if the battery is at full charge
|
||||
pub hide_on_full_charge: Option<bool>,
|
||||
/// Data refresh interval (default: 10 seconds)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
@@ -33,6 +36,7 @@ impl From<BatteryConfig> for Battery {
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
hide_on_full_charge: value.hide_on_full_charge.unwrap_or(false),
|
||||
manager: Manager::new().unwrap(),
|
||||
last_state: String::new(),
|
||||
data_refresh_interval,
|
||||
@@ -52,6 +56,7 @@ pub enum BatteryState {
|
||||
|
||||
pub struct Battery {
|
||||
pub enable: bool,
|
||||
hide_on_full_charge: bool,
|
||||
manager: Manager,
|
||||
pub state: BatteryState,
|
||||
data_refresh_interval: u64,
|
||||
@@ -71,17 +76,22 @@ impl Battery {
|
||||
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}%")
|
||||
if percentage == 100.0 && self.hide_on_full_charge {
|
||||
output = String::new()
|
||||
} else {
|
||||
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}%"),
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Icon => format!("{percentage:.0}%"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,12 +135,18 @@ impl BarWidget for Battery {
|
||||
},
|
||||
);
|
||||
|
||||
config.apply_on_widget(true, ui, |ui| {
|
||||
ui.add(
|
||||
Label::new(layout_job)
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
);
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("cmd.exe")
|
||||
.args(["/C", "start", "ms-settings:batterysaver"])
|
||||
.spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.32`
|
||||
/// The `komorebi.bar.json` configuration file reference for `v0.1.33`
|
||||
pub struct KomobarConfig {
|
||||
/// Bar positioning options
|
||||
#[serde(alias = "viewport")]
|
||||
|
||||
@@ -13,6 +13,7 @@ mod selected_frame;
|
||||
mod storage;
|
||||
mod time;
|
||||
mod ui;
|
||||
mod update;
|
||||
mod widget;
|
||||
|
||||
use crate::bar::Komobar;
|
||||
|
||||
@@ -100,7 +100,7 @@ impl Network {
|
||||
if let Some(friendly_name) = &interface.friendly_name {
|
||||
self.default_interface.clone_from(friendly_name);
|
||||
|
||||
self.networks_network_activity.refresh();
|
||||
self.networks_network_activity.refresh(true);
|
||||
|
||||
for (interface_name, data) in &self.networks_network_activity {
|
||||
if friendly_name.eq(interface_name) {
|
||||
|
||||
@@ -50,7 +50,7 @@ impl Storage {
|
||||
fn output(&mut self) -> Vec<String> {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(self.data_refresh_interval) {
|
||||
self.disks.refresh();
|
||||
self.disks.refresh(true);
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
|
||||
158
komorebi-bar/src/update.rs
Normal file
158
komorebi-bar/src/update.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use crate::config::LabelPrefix;
|
||||
use crate::render::RenderConfig;
|
||||
use crate::selected_frame::SelectableFrame;
|
||||
use crate::widget::BarWidget;
|
||||
use eframe::egui::text::LayoutJob;
|
||||
use eframe::egui::Align;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Label;
|
||||
use eframe::egui::TextFormat;
|
||||
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;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct UpdateConfig {
|
||||
/// Enable the Update widget
|
||||
pub enable: bool,
|
||||
/// Data refresh interval (default: 12 hours)
|
||||
pub data_refresh_interval: Option<u64>,
|
||||
/// Display label prefix
|
||||
pub label_prefix: Option<LabelPrefix>,
|
||||
}
|
||||
|
||||
impl From<UpdateConfig> for Update {
|
||||
fn from(value: UpdateConfig) -> Self {
|
||||
let data_refresh_interval = value.data_refresh_interval.unwrap_or(12);
|
||||
|
||||
let mut latest_version = String::new();
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
if let Ok(response) = client
|
||||
.get("https://api.github.com/repos/LGUG2Z/komorebi/releases/latest")
|
||||
.header("User-Agent", "komorebi-bar-version-checker")
|
||||
.send()
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Release {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
if let Ok(release) =
|
||||
serde_json::from_str::<Release>(&response.text().unwrap_or_default())
|
||||
{
|
||||
let trimmed = release.tag_name.trim_start_matches("v");
|
||||
latest_version = trimmed.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
data_refresh_interval,
|
||||
installed_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
latest_version,
|
||||
label_prefix: value.label_prefix.unwrap_or(LabelPrefix::IconAndText),
|
||||
last_updated: Instant::now()
|
||||
.checked_sub(Duration::from_secs(data_refresh_interval))
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Update {
|
||||
pub enable: bool,
|
||||
data_refresh_interval: u64,
|
||||
installed_version: String,
|
||||
latest_version: String,
|
||||
label_prefix: LabelPrefix,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl Update {
|
||||
fn output(&mut self) -> String {
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated)
|
||||
> Duration::from_secs((self.data_refresh_interval * 60) * 60)
|
||||
{
|
||||
let client = reqwest::blocking::Client::new();
|
||||
if let Ok(response) = client
|
||||
.get("https://api.github.com/repos/LGUG2Z/komorebi/releases/latest")
|
||||
.header("User-Agent", "komorebi-bar-version-checker")
|
||||
.send()
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct Release {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
if let Ok(release) =
|
||||
serde_json::from_str::<Release>(&response.text().unwrap_or_default())
|
||||
{
|
||||
let trimmed = release.tag_name.trim_start_matches("v");
|
||||
self.latest_version = trimmed.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
if self.latest_version > self.installed_version {
|
||||
format!("Update available! v{}", self.latest_version)
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BarWidget for Update {
|
||||
fn render(&mut self, ctx: &Context, ui: &mut Ui, config: &mut RenderConfig) {
|
||||
if self.enable {
|
||||
let output = self.output();
|
||||
if !output.is_empty() {
|
||||
let mut layout_job = LayoutJob::simple(
|
||||
match self.label_prefix {
|
||||
LabelPrefix::Icon | LabelPrefix::IconAndText => {
|
||||
egui_phosphor::regular::ROCKET_LAUNCH.to_string()
|
||||
}
|
||||
LabelPrefix::None | LabelPrefix::Text => String::new(),
|
||||
},
|
||||
config.icon_font_id.clone(),
|
||||
ctx.style().visuals.selection.stroke.color,
|
||||
100.0,
|
||||
);
|
||||
|
||||
layout_job.append(
|
||||
&output,
|
||||
10.0,
|
||||
TextFormat {
|
||||
font_id: config.text_font_id.clone(),
|
||||
color: ctx.style().visuals.text_color(),
|
||||
valign: Align::Center,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
config.apply_on_widget(false, ui, |ui| {
|
||||
if SelectableFrame::new(false)
|
||||
.show(ui, |ui| ui.add(Label::new(layout_job).selectable(false)))
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) = Command::new("explorer.exe")
|
||||
.args([format!(
|
||||
"https://github.com/LGUG2Z/komorebi/releases/v{}",
|
||||
self.latest_version
|
||||
)])
|
||||
.spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ use crate::storage::Storage;
|
||||
use crate::storage::StorageConfig;
|
||||
use crate::time::Time;
|
||||
use crate::time::TimeConfig;
|
||||
use crate::update::Update;
|
||||
use crate::update::UpdateConfig;
|
||||
use eframe::egui::Context;
|
||||
use eframe::egui::Ui;
|
||||
use schemars::JsonSchema;
|
||||
@@ -38,6 +40,7 @@ pub enum WidgetConfig {
|
||||
Network(NetworkConfig),
|
||||
Storage(StorageConfig),
|
||||
Time(TimeConfig),
|
||||
Update(UpdateConfig),
|
||||
}
|
||||
|
||||
impl WidgetConfig {
|
||||
@@ -52,6 +55,7 @@ impl WidgetConfig {
|
||||
WidgetConfig::Network(config) => Box::new(Network::from(*config)),
|
||||
WidgetConfig::Storage(config) => Box::new(Storage::from(*config)),
|
||||
WidgetConfig::Time(config) => Box::new(Time::from(config.clone())),
|
||||
WidgetConfig::Update(config) => Box::new(Update::from(*config)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,19 +65,20 @@ impl WidgetConfig {
|
||||
WidgetConfig::Cpu(config) => config.enable,
|
||||
WidgetConfig::Date(config) => config.enable,
|
||||
WidgetConfig::Komorebi(config) => {
|
||||
config.workspaces.as_ref().map_or(false, |w| w.enable)
|
||||
|| config.layout.as_ref().map_or(false, |w| w.enable)
|
||||
|| config.focused_window.as_ref().map_or(false, |w| w.enable)
|
||||
config.workspaces.as_ref().is_some_and(|w| w.enable)
|
||||
|| config.layout.as_ref().is_some_and(|w| w.enable)
|
||||
|| config.focused_window.as_ref().is_some_and(|w| w.enable)
|
||||
|| config
|
||||
.configuration_switcher
|
||||
.as_ref()
|
||||
.map_or(false, |w| w.enable)
|
||||
.is_some_and(|w| w.enable)
|
||||
}
|
||||
WidgetConfig::Media(config) => config.enable,
|
||||
WidgetConfig::Memory(config) => config.enable,
|
||||
WidgetConfig::Network(config) => config.enable,
|
||||
WidgetConfig::Storage(config) => config.enable,
|
||||
WidgetConfig::Time(config) => config.enable,
|
||||
WidgetConfig::Update(config) => config.enable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-client"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-gui"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "komorebi-themes"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
[package]
|
||||
name = "komorebi"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
description = "A tiling window manager for Windows"
|
||||
categories = ["tiling-window-manager", "windows"]
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
|
||||
@@ -48,7 +47,7 @@ windows-core = { workspace = true }
|
||||
windows-implement = { workspace = true }
|
||||
windows-interface = { workspace = true }
|
||||
winput = "0.2"
|
||||
winreg = "0.52"
|
||||
winreg = "0.53"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { workspace = true }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use shadow_rs::ShadowBuilder;
|
||||
|
||||
fn main() {
|
||||
shadow_rs::new().unwrap();
|
||||
ShadowBuilder::builder().build().unwrap();
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
borders.remove(id);
|
||||
}
|
||||
|
||||
for (idx, c) in ws.containers().iter().enumerate() {
|
||||
'containers: for (idx, c) in ws.containers().iter().enumerate() {
|
||||
// Get the border entry for this container from the map or create one
|
||||
let mut new_border = false;
|
||||
let border = match borders.entry(c.id().clone()) {
|
||||
@@ -471,7 +471,17 @@ pub fn handle_notifications(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result
|
||||
let reference_hwnd =
|
||||
c.focused_window().copied().unwrap_or_default().hwnd;
|
||||
|
||||
let rect = WindowsApi::window_rect(reference_hwnd)?;
|
||||
// avoid getting into a thread restart loop if we try to look up
|
||||
// rect info for a window that has been destroyed by the time
|
||||
// we get here
|
||||
let rect = match WindowsApi::window_rect(reference_hwnd) {
|
||||
Ok(rect) => rect,
|
||||
Err(_) => {
|
||||
let _ = border.destroy();
|
||||
borders.remove(c.id());
|
||||
continue 'containers;
|
||||
}
|
||||
};
|
||||
|
||||
let should_invalidate = match last_focus_state {
|
||||
None => true,
|
||||
|
||||
@@ -406,7 +406,7 @@ impl Direction for CustomLayout {
|
||||
}
|
||||
|
||||
let (column_idx, column) = self.column_with_idx(idx);
|
||||
column.map_or(false, |column| match column {
|
||||
column.is_some_and(|column| match column {
|
||||
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
|
||||
| Column::Tertiary(ColumnSplit::Horizontal) => {
|
||||
self.column_for_container_idx(idx - 1) == column_idx
|
||||
@@ -420,7 +420,7 @@ impl Direction for CustomLayout {
|
||||
}
|
||||
|
||||
let (column_idx, column) = self.column_with_idx(idx);
|
||||
column.map_or(false, |column| match column {
|
||||
column.is_some_and(|column| match column {
|
||||
Column::Secondary(Some(ColumnSplitWithCapacity::Horizontal(_)))
|
||||
| Column::Tertiary(ColumnSplit::Horizontal) => {
|
||||
self.column_for_container_idx(idx + 1) == column_idx
|
||||
|
||||
@@ -238,7 +238,9 @@ pub struct SubscribeOptions {
|
||||
pub filter_state_changes: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Display, Serialize, Deserialize, JsonSchema)]
|
||||
#[derive(
|
||||
Debug, Copy, Clone, Eq, PartialEq, Display, Serialize, Deserialize, JsonSchema, ValueEnum,
|
||||
)]
|
||||
pub enum StackbarMode {
|
||||
Always,
|
||||
Never,
|
||||
|
||||
@@ -298,6 +298,7 @@ pub fn notify_subscribers(notification: Notification, state_has_been_modified: b
|
||||
| NotificationEvent::Socket(SocketMessage::ReloadStaticConfiguration(_))
|
||||
| NotificationEvent::WindowManager(WindowManagerEvent::TitleUpdate(_, _))
|
||||
| NotificationEvent::WindowManager(WindowManagerEvent::Show(_, _))
|
||||
| NotificationEvent::WindowManager(WindowManagerEvent::Uncloak(_, _))
|
||||
);
|
||||
|
||||
let notification = &serde_json::to_string(¬ification)?;
|
||||
|
||||
@@ -177,7 +177,7 @@ fn main() -> Result<()> {
|
||||
SESSION_ID.store(session_id, Ordering::SeqCst);
|
||||
|
||||
let mut system = sysinfo::System::new_all();
|
||||
system.refresh_processes(ProcessesToUpdate::All);
|
||||
system.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
let matched_procs: Vec<&Process> = system.processes_by_name("komorebi.exe".as_ref()).collect();
|
||||
|
||||
|
||||
@@ -1662,6 +1662,7 @@ impl WindowManager {
|
||||
}
|
||||
SocketMessage::StackbarMode(mode) => {
|
||||
STACKBAR_MODE.store(mode);
|
||||
self.retile_all(true)?;
|
||||
}
|
||||
SocketMessage::StackbarLabel(label) => {
|
||||
STACKBAR_LABEL.store(label);
|
||||
|
||||
@@ -34,6 +34,8 @@ pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||
let mut wm = arc.lock();
|
||||
let offset = wm.work_area_offset;
|
||||
|
||||
let mut update_borders = false;
|
||||
|
||||
for (i, monitor) in wm.monitors_mut().iter_mut().enumerate() {
|
||||
let work_area = *monitor.work_area_size();
|
||||
let window_based_work_area_offset = (
|
||||
@@ -51,7 +53,7 @@ pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||
let reaped_orphans = workspace.reap_orphans()?;
|
||||
if reaped_orphans.0 > 0 || reaped_orphans.1 > 0 {
|
||||
workspace.update(&work_area, offset, window_based_work_area_offset)?;
|
||||
border_manager::send_notification(None);
|
||||
update_borders = true;
|
||||
tracing::info!(
|
||||
"reaped {} orphan window(s) and {} orphaned container(s) on monitor: {}, workspace: {}",
|
||||
reaped_orphans.0,
|
||||
@@ -62,5 +64,9 @@ pub fn find_orphans(wm: Arc<Mutex<WindowManager>>) -> color_eyre::Result<()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if update_borders {
|
||||
border_manager::send_notification(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ use crate::window_manager::WindowManager;
|
||||
use crate::window_manager_event::WindowManagerEvent;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::workspace::Workspace;
|
||||
use crate::Axis;
|
||||
use crate::CrossBoundaryBehaviour;
|
||||
use crate::DATA_DIR;
|
||||
use crate::DEFAULT_CONTAINER_PADDING;
|
||||
@@ -148,6 +149,9 @@ pub struct WorkspaceConfig {
|
||||
/// (default: false)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub float_override: Option<bool>,
|
||||
/// Specify an axis on which to flip the selected layout (default: None)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub layout_flip: Option<Axis>,
|
||||
}
|
||||
|
||||
impl From<&Workspace> for WorkspaceConfig {
|
||||
@@ -202,6 +206,7 @@ impl From<&Workspace> for WorkspaceConfig {
|
||||
apply_window_based_work_area_offset: Some(value.apply_window_based_work_area_offset()),
|
||||
window_container_behaviour: *value.window_container_behaviour(),
|
||||
float_override: *value.float_override(),
|
||||
layout_flip: value.layout_flip(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -238,7 +243,7 @@ impl From<&Monitor> for MonitorConfig {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||
/// The `komorebi.json` static configuration file reference for `v0.1.32`
|
||||
/// The `komorebi.json` static configuration file reference for `v0.1.33`
|
||||
pub struct StaticConfig {
|
||||
/// DEPRECATED from v0.1.22: no longer required
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
||||
@@ -435,13 +435,22 @@ impl WindowManager {
|
||||
if let Some(state_monitor) = state.monitors.elements().get(monitor_idx) {
|
||||
if let Some(state_workspace) = state_monitor.workspaces().get(workspace_idx)
|
||||
{
|
||||
// to make sure padding changes get applied for users after a quick restart
|
||||
let container_padding = workspace.container_padding();
|
||||
let workspace_padding = workspace.workspace_padding();
|
||||
|
||||
*workspace = state_workspace.clone();
|
||||
|
||||
workspace.set_container_padding(container_padding);
|
||||
workspace.set_workspace_padding(workspace_padding);
|
||||
|
||||
if state_monitor.focused_workspace_idx() == workspace_idx {
|
||||
focused_workspace = workspace_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(error) = monitor.focus_workspace(focused_workspace) {
|
||||
tracing::warn!(
|
||||
"cannot focus workspace '{focused_workspace}' on monitor '{monitor_idx}' from {}: {}",
|
||||
@@ -449,6 +458,7 @@ impl WindowManager {
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(error) = monitor.load_focused_workspace(mouse_follows_focus) {
|
||||
tracing::warn!(
|
||||
"cannot load focused workspace '{focused_workspace}' on monitor '{monitor_idx}' from {}: {}",
|
||||
@@ -456,6 +466,7 @@ impl WindowManager {
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
if let Err(error) = monitor.update_focused_workspace(offset) {
|
||||
tracing::warn!(
|
||||
"cannot update workspace '{focused_workspace}' on monitor '{monitor_idx}' from {}: {}",
|
||||
@@ -720,75 +731,80 @@ impl WindowManager {
|
||||
.ok_or_else(|| anyhow!("there is no monitor with that index"))?
|
||||
.focused_workspace_idx();
|
||||
|
||||
let workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock();
|
||||
let regex_identifiers = REGEX_IDENTIFIERS.lock();
|
||||
// Go through all the monitors and workspaces
|
||||
for (i, monitor) in self.monitors().iter().enumerate() {
|
||||
for (j, workspace) in monitor.workspaces().iter().enumerate() {
|
||||
// And all the visible windows (at the top of a container)
|
||||
for window in workspace.visible_windows().into_iter().flatten() {
|
||||
let mut already_moved_window_handles = self.already_moved_window_handles.lock();
|
||||
let exe_name = window.exe()?;
|
||||
let title = window.title()?;
|
||||
let class = window.class()?;
|
||||
let path = window.path()?;
|
||||
// scope mutex locks to avoid deadlock if should_update_focused_workspace evaluates to true
|
||||
// at the end of this function
|
||||
{
|
||||
let workspace_matching_rules = WORKSPACE_MATCHING_RULES.lock();
|
||||
let regex_identifiers = REGEX_IDENTIFIERS.lock();
|
||||
// Go through all the monitors and workspaces
|
||||
for (i, monitor) in self.monitors().iter().enumerate() {
|
||||
for (j, workspace) in monitor.workspaces().iter().enumerate() {
|
||||
// And all the visible windows (at the top of a container)
|
||||
for window in workspace.visible_windows().into_iter().flatten() {
|
||||
let mut already_moved_window_handles =
|
||||
self.already_moved_window_handles.lock();
|
||||
|
||||
for rule in &*workspace_matching_rules {
|
||||
let matched = match &rule.matching_rule {
|
||||
MatchingRule::Simple(r) => should_act_individual(
|
||||
&title,
|
||||
&exe_name,
|
||||
&class,
|
||||
&path,
|
||||
r,
|
||||
®ex_identifiers,
|
||||
),
|
||||
MatchingRule::Composite(r) => {
|
||||
let mut composite_results = vec![];
|
||||
for identifier in r {
|
||||
composite_results.push(should_act_individual(
|
||||
if let (Ok(exe_name), Ok(title), Ok(class), Ok(path)) =
|
||||
(window.exe(), window.title(), window.class(), window.path())
|
||||
{
|
||||
for rule in &*workspace_matching_rules {
|
||||
let matched = match &rule.matching_rule {
|
||||
MatchingRule::Simple(r) => should_act_individual(
|
||||
&title,
|
||||
&exe_name,
|
||||
&class,
|
||||
&path,
|
||||
identifier,
|
||||
r,
|
||||
®ex_identifiers,
|
||||
));
|
||||
),
|
||||
MatchingRule::Composite(r) => {
|
||||
let mut composite_results = vec![];
|
||||
for identifier in r {
|
||||
composite_results.push(should_act_individual(
|
||||
&title,
|
||||
&exe_name,
|
||||
&class,
|
||||
&path,
|
||||
identifier,
|
||||
®ex_identifiers,
|
||||
));
|
||||
}
|
||||
|
||||
composite_results.iter().all(|&x| x)
|
||||
}
|
||||
};
|
||||
|
||||
if matched {
|
||||
let floating = workspace.floating_windows().contains(window);
|
||||
|
||||
if rule.initial_only {
|
||||
if !already_moved_window_handles.contains(&window.hwnd) {
|
||||
already_moved_window_handles.insert(window.hwnd);
|
||||
|
||||
self.add_window_handle_to_move_based_on_workspace_rule(
|
||||
&window.title()?,
|
||||
window.hwnd,
|
||||
i,
|
||||
j,
|
||||
rule.monitor_index,
|
||||
rule.workspace_index,
|
||||
floating,
|
||||
&mut to_move,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.add_window_handle_to_move_based_on_workspace_rule(
|
||||
&window.title()?,
|
||||
window.hwnd,
|
||||
i,
|
||||
j,
|
||||
rule.monitor_index,
|
||||
rule.workspace_index,
|
||||
floating,
|
||||
&mut to_move,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
composite_results.iter().all(|&x| x)
|
||||
}
|
||||
};
|
||||
|
||||
if matched {
|
||||
let floating = workspace.floating_windows().contains(window);
|
||||
|
||||
if rule.initial_only {
|
||||
if !already_moved_window_handles.contains(&window.hwnd) {
|
||||
already_moved_window_handles.insert(window.hwnd);
|
||||
|
||||
self.add_window_handle_to_move_based_on_workspace_rule(
|
||||
&window.title()?,
|
||||
window.hwnd,
|
||||
i,
|
||||
j,
|
||||
rule.monitor_index,
|
||||
rule.workspace_index,
|
||||
floating,
|
||||
&mut to_move,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.add_window_handle_to_move_based_on_workspace_rule(
|
||||
&window.title()?,
|
||||
window.hwnd,
|
||||
i,
|
||||
j,
|
||||
rule.monitor_index,
|
||||
rule.workspace_index,
|
||||
floating,
|
||||
&mut to_move,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,10 +133,14 @@ impl Workspace {
|
||||
|
||||
if config.container_padding.is_some() {
|
||||
self.set_container_padding(config.container_padding);
|
||||
} else {
|
||||
self.set_container_padding(Some(DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst)));
|
||||
}
|
||||
|
||||
if config.workspace_padding.is_some() {
|
||||
self.set_workspace_padding(config.workspace_padding);
|
||||
} else {
|
||||
self.set_container_padding(Some(DEFAULT_WORKSPACE_PADDING.load(Ordering::SeqCst)));
|
||||
}
|
||||
|
||||
if let Some(layout) = &config.layout {
|
||||
@@ -187,6 +191,10 @@ impl Workspace {
|
||||
self.set_float_override(config.float_override);
|
||||
}
|
||||
|
||||
if config.layout_flip.is_some() {
|
||||
self.set_layout_flip(config.layout_flip);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
[package]
|
||||
name = "komorebic-no-console"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
description = "The command-line interface (without a console) for Komorebi, a tiling window manager for Windows"
|
||||
categories = ["cli", "tiling-window-manager", "windows"]
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
[package]
|
||||
name = "komorebic"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
description = "The command-line interface for Komorebi, a tiling window manager for Windows"
|
||||
categories = ["cli", "tiling-window-manager", "windows"]
|
||||
repository = "https://github.com/LGUG2Z/komorebi"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use shadow_rs::ShadowBuilder;
|
||||
|
||||
fn main() {
|
||||
if std::fs::metadata("applications.json").is_err() {
|
||||
let applications_json = reqwest::blocking::get(
|
||||
@@ -6,5 +8,5 @@ fn main() {
|
||||
std::fs::write("applications.json", applications_json).unwrap();
|
||||
}
|
||||
|
||||
shadow_rs::new().unwrap();
|
||||
ShadowBuilder::builder().build().unwrap();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ use miette::SourceSpan;
|
||||
use paste::paste;
|
||||
use schemars::gen::SchemaSettings;
|
||||
use schemars::schema_for;
|
||||
use serde::Deserialize;
|
||||
use sysinfo::ProcessesToUpdate;
|
||||
use which::which;
|
||||
use windows::Win32::Foundation::HWND;
|
||||
@@ -720,6 +721,13 @@ struct BorderImplementation {
|
||||
style: komorebi_client::BorderImplementation,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct StackbarMode {
|
||||
/// Desired stackbar mode
|
||||
#[clap(value_enum)]
|
||||
mode: komorebi_client::StackbarMode,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Animation {
|
||||
#[clap(value_enum)]
|
||||
@@ -921,6 +929,13 @@ struct EnableAutostart {
|
||||
masir: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Check {
|
||||
/// Path to a static configuration JSON file
|
||||
#[clap(action, short, long)]
|
||||
komorebi_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct ReplaceConfiguration {
|
||||
/// Static configuration JSON file from which the configuration should be loaded
|
||||
@@ -953,7 +968,7 @@ enum SubCommand {
|
||||
/// Kill background processes started by komorebic
|
||||
Kill(Kill),
|
||||
/// Check komorebi configuration and related files for common errors
|
||||
Check,
|
||||
Check(Check),
|
||||
/// Show the path to komorebi.json
|
||||
#[clap(alias = "config")]
|
||||
Configuration,
|
||||
@@ -1360,6 +1375,9 @@ enum SubCommand {
|
||||
/// Set the border implementation
|
||||
#[clap(arg_required_else_help = true)]
|
||||
BorderImplementation(BorderImplementation),
|
||||
/// Set the stackbar mode
|
||||
#[clap(arg_required_else_help = true)]
|
||||
StackbarMode(StackbarMode),
|
||||
/// Enable or disable transparency for unfocused windows
|
||||
#[clap(arg_required_else_help = true)]
|
||||
Transparency(Transparency),
|
||||
@@ -1575,7 +1593,7 @@ fn main() -> Result<()> {
|
||||
std::fs::remove_file(shortcut_file)?;
|
||||
}
|
||||
}
|
||||
SubCommand::Check => {
|
||||
SubCommand::Check(args) => {
|
||||
let home_display = HOME_DIR.display();
|
||||
if HAS_CUSTOM_CONFIG_HOME.load(Ordering::SeqCst) {
|
||||
println!("KOMOREBI_CONFIG_HOME detected: {home_display}\n");
|
||||
@@ -1590,7 +1608,15 @@ fn main() -> Result<()> {
|
||||
|
||||
println!("Looking for configuration files in {home_display}\n");
|
||||
|
||||
let static_config = HOME_DIR.join("komorebi.json");
|
||||
let static_config = if let Some(static_config) = args.komorebi_config {
|
||||
println!(
|
||||
"Using an arbitrary configuration file passed to --komorebi-config flag\n"
|
||||
);
|
||||
static_config
|
||||
} else {
|
||||
HOME_DIR.join("komorebi.json")
|
||||
};
|
||||
|
||||
let config_pwsh = HOME_DIR.join("komorebi.ps1");
|
||||
let config_ahk = HOME_DIR.join("komorebi.ahk");
|
||||
let config_whkd = WHKD_CONFIG_DIR.join("whkdrc");
|
||||
@@ -1667,6 +1693,30 @@ fn main() -> Result<()> {
|
||||
println!("No komorebi configuration found in {home_display}\n");
|
||||
println!("If running 'komorebic start --await-configuration', you will manually have to call the following command to begin tiling: komorebic complete-configuration\n");
|
||||
}
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
if let Ok(response) = client
|
||||
.get("https://api.github.com/repos/LGUG2Z/komorebi/releases/latest")
|
||||
.header("User-Agent", "komorebic-version-checker")
|
||||
.send()
|
||||
{
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Release {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
if let Ok(release) =
|
||||
serde_json::from_str::<Release>(&response.text().unwrap_or_default())
|
||||
{
|
||||
let trimmed = release.tag_name.trim_start_matches("v");
|
||||
if trimmed > version {
|
||||
println!("An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SubCommand::Configuration => {
|
||||
let static_config = HOME_DIR.join("komorebi.json");
|
||||
@@ -2050,7 +2100,7 @@ fn main() -> Result<()> {
|
||||
};
|
||||
|
||||
let mut system = sysinfo::System::new_all();
|
||||
system.refresh_processes(ProcessesToUpdate::All);
|
||||
system.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
let mut attempts = 0;
|
||||
let mut running = system
|
||||
@@ -2071,7 +2121,7 @@ fn main() -> Result<()> {
|
||||
print!("Waiting for komorebi.exe to start...");
|
||||
std::thread::sleep(Duration::from_secs(3));
|
||||
|
||||
system.refresh_processes(ProcessesToUpdate::All);
|
||||
system.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
if system
|
||||
.processes_by_name("komorebi.exe".as_ref())
|
||||
@@ -2207,14 +2257,16 @@ if (!(Get-Process masir -ErrorAction SilentlyContinue))
|
||||
}
|
||||
|
||||
println!("\nThank you for using komorebi!\n");
|
||||
println!("# Sponsorship");
|
||||
println!("# Commercial Use License");
|
||||
println!("* View licensing options https://lgug2z.com/software/komorebi - A commercial use license is required to use komorebi at work");
|
||||
println!("\n# Personal Use Sponsorship");
|
||||
println!("* Become a sponsor https://github.com/sponsors/LGUG2Z - $5/month makes a big difference");
|
||||
println!("* Leave a tip https://ko-fi.com/lgug2z - An alternative to GitHub Sponsors");
|
||||
println!("\n# Community");
|
||||
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
|
||||
println!(
|
||||
"* Subscribe to https://youtube.com/@LGUG2Z - Development videos, feature previews and release overviews"
|
||||
);
|
||||
println!("\n# Community");
|
||||
println!("* Join the Discord https://discord.gg/mGkn66PHkx - Chat, ask questions, share your desktops");
|
||||
println!("* Explore the Awesome Komorebi list https://github.com/LGUG2Z/awesome-komorebi - Projects in the komorebi ecosystem");
|
||||
println!("\n# Documentation");
|
||||
println!("* Read the docs https://lgug2z.github.io/komorebi - Quickly search through all komorebic commands");
|
||||
@@ -2244,6 +2296,30 @@ if (!(Get-Process masir -ErrorAction SilentlyContinue))
|
||||
let stdout = String::from_utf8(output.stdout)?;
|
||||
println!("{stdout}");
|
||||
}
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
|
||||
if let Ok(response) = client
|
||||
.get("https://api.github.com/repos/LGUG2Z/komorebi/releases/latest")
|
||||
.header("User-Agent", "komorebic-version-checker")
|
||||
.send()
|
||||
{
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Release {
|
||||
tag_name: String,
|
||||
}
|
||||
|
||||
if let Ok(release) =
|
||||
serde_json::from_str::<Release>(&response.text().unwrap_or_default())
|
||||
{
|
||||
let trimmed = release.tag_name.trim_start_matches("v");
|
||||
if trimmed > version {
|
||||
println!("An updated version of komorebi is available! https://github.com/LGUG2Z/komorebi/releases/v{trimmed}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
SubCommand::Stop(arg) => {
|
||||
if arg.whkd {
|
||||
@@ -2323,7 +2399,7 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
send_message(&SocketMessage::Stop)?;
|
||||
}
|
||||
let mut system = sysinfo::System::new_all();
|
||||
system.refresh_processes(ProcessesToUpdate::All);
|
||||
system.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
if system.processes_by_name("komorebi.exe".as_ref()).count() >= 1 {
|
||||
println!("komorebi is still running, attempting to force-quit");
|
||||
@@ -2719,6 +2795,9 @@ if (Get-Command Get-CimInstance -ErrorAction SilentlyContinue) {
|
||||
SubCommand::BorderImplementation(arg) => {
|
||||
send_message(&SocketMessage::BorderImplementation(arg.style))?;
|
||||
}
|
||||
SubCommand::StackbarMode(arg) => {
|
||||
send_message(&SocketMessage::StackbarMode(arg.mode))?;
|
||||
}
|
||||
SubCommand::Transparency(arg) => {
|
||||
send_message(&SocketMessage::Transparency(arg.boolean_state.into()))?;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ nav:
|
||||
- cli/quickstart.md
|
||||
- cli/start.md
|
||||
- cli/stop.md
|
||||
- cli/kill.md
|
||||
- cli/check.md
|
||||
- cli/configuration.md
|
||||
- cli/bar-configuration.md
|
||||
@@ -103,9 +104,11 @@ nav:
|
||||
- cli/force-focus.md
|
||||
- cli/cycle-focus.md
|
||||
- cli/cycle-move.md
|
||||
- cli/eager-focus.md
|
||||
- cli/stack.md
|
||||
- cli/unstack.md
|
||||
- cli/cycle-stack.md
|
||||
- cli/cycle-stack-index.md
|
||||
- cli/focus-stack-window.md
|
||||
- cli/stack-all.md
|
||||
- cli/unstack-all.md
|
||||
@@ -129,6 +132,7 @@ nav:
|
||||
- cli/focus-workspaces.md
|
||||
- cli/focus-monitor-workspace.md
|
||||
- cli/focus-named-workspace.md
|
||||
- cli/close-workspace.md
|
||||
- cli/cycle-monitor.md
|
||||
- cli/cycle-workspace.md
|
||||
- cli/move-workspace-to-monitor.md
|
||||
@@ -196,6 +200,7 @@ nav:
|
||||
- cli/clear-workspace-rules.md
|
||||
- cli/clear-named-workspace-rules.md
|
||||
- cli/clear-all-workspace-rules.md
|
||||
- cli/enforce-workspace-rules.md
|
||||
- cli/identify-object-name-change-application.md
|
||||
- cli/identify-tray-application.md
|
||||
- cli/identify-layered-application.md
|
||||
@@ -207,6 +212,7 @@ nav:
|
||||
- cli/border-offset.md
|
||||
- cli/border-style.md
|
||||
- cli/border-implementation.md
|
||||
- cli/stackbar-mode.md
|
||||
- cli/transparency.md
|
||||
- cli/transparency-alpha.md
|
||||
- cli/toggle-transparency.md
|
||||
|
||||
194
schema.bar.json
194
schema.bar.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "KomobarConfig",
|
||||
"description": "The `komorebi.bar.json` configuration file reference for `v0.1.32`",
|
||||
"description": "The `komorebi.bar.json` configuration file reference for `v0.1.33`",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"left_widgets",
|
||||
@@ -36,6 +36,10 @@
|
||||
"description": "Enable the Battery widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_on_full_charge": {
|
||||
"description": "Hide the widget if the battery is at full charge",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
@@ -778,6 +782,66 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Update"
|
||||
],
|
||||
"properties": {
|
||||
"Update": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"data_refresh_interval": {
|
||||
"description": "Data refresh interval (default: 12 hours)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"enable": {
|
||||
"description": "Enable the Update widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Show no prefix",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Icon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon and text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"IconAndText"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1154,6 +1218,10 @@
|
||||
"description": "Enable the Battery widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_on_full_charge": {
|
||||
"description": "Hide the widget if the battery is at full charge",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
@@ -1896,6 +1964,66 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Update"
|
||||
],
|
||||
"properties": {
|
||||
"Update": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"data_refresh_interval": {
|
||||
"description": "Data refresh interval (default: 12 hours)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"enable": {
|
||||
"description": "Enable the Update widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Show no prefix",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Icon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon and text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"IconAndText"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2025,6 +2153,10 @@
|
||||
"description": "Enable the Battery widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_on_full_charge": {
|
||||
"description": "Hide the widget if the battery is at full charge",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
@@ -2767,6 +2899,66 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"Update"
|
||||
],
|
||||
"properties": {
|
||||
"Update": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"enable"
|
||||
],
|
||||
"properties": {
|
||||
"data_refresh_interval": {
|
||||
"description": "Data refresh interval (default: 12 hours)",
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"enable": {
|
||||
"description": "Enable the Update widget",
|
||||
"type": "boolean"
|
||||
},
|
||||
"label_prefix": {
|
||||
"description": "Display label prefix",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Show no prefix",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"None"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Icon"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Show an icon and text",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"IconAndText"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
11
schema.json
11
schema.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "StaticConfig",
|
||||
"description": "The `komorebi.json` static configuration file reference for `v0.1.32`",
|
||||
"description": "The `komorebi.json` static configuration file reference for `v0.1.33`",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"animation": {
|
||||
@@ -1227,6 +1227,15 @@
|
||||
"RightMainVerticalStack"
|
||||
]
|
||||
},
|
||||
"layout_flip": {
|
||||
"description": "Specify an axis on which to flip the selected layout (default: None)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Horizontal",
|
||||
"Vertical",
|
||||
"HorizontalAndVertical"
|
||||
]
|
||||
},
|
||||
"layout_rules": {
|
||||
"description": "Layout rules (default: None)",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user