I think this got broken as part of the automatic Rust version syntax
upgrades which joined two if clauses together with && which should have
been kept separate.
Now, if the user gives the --bar flag to the start command, and a static
config file is not resolved, or if the static config file does not have
a bar_configurations stanza, it will fallthrough to the default
PowerShell snippet to try and start komorebi-bar without an explicit
--config flag.
This commit fixes the stupidest of stupid bugs. Column width
calculations on the Scrolling layout should take the number of windows
into account, especially when lower than the configured column count.
I was getting really tired of having to switch between display inputs to
different platform-specific machines to be able to make and test changes
on komorebi for Windows and komorebi for Mac.
With this commit, the `flake.nix` provides a Nix devShell and crane
build for users to make and validate changes with `cargo check`, `cargo
clippy` and `cargo build` with the Windows MSVC toolchain on Linux and
macOS.
Getting tired of making little changes in both this and the komorebi for
Mac repo - I think eventually either komorebi-themes will live in its
own repo or komorebi for Mac will be integrated here.
But for now, at least everything is defined in komorebi-themes and I
don't have to redefine any theme-related stuff in komorebi for Mac.
This commit ensures that the various default values that the different
Option<T> config properties can be unwrapped to are encoded by schemars
so that they can be picked up by docgen.
This commit ensures that we are using show_total_activity and
show_activity instead of show_total_data_transmitted and
show_network_activity in the komorebi.bar.example.json file which is
populated for users in the quickstart command.
JSONSchema is not smart enough to resolve aliases for backwards compat,
which results in confusing behaviour for new users trying to edit this
file.
fix#1596
This commit makes the komorebic promote-swap command reversible by
storing the previous container index in the Workspace state.
If the current container index is the same as the layout's primary
index, and there is a previous promotion swap container index available,
the two containers at those indices will be swapped, effectively making
a second call to promote-swap an undo.
This commit adds a new SocketMessage variant PromoteSwap and
corresponding komorebic promote-swap command.
PromoteSwap will also promote the focused window container to the
largest tile in the layout, but instead of removing the focused
container x from the Ring and inserting it into position 0, possibly
changing the positions of other windows between 0 and x, the indices x
and 0 in the Ring will be swapped directly.
This commit adds visual feedback in the form of a ghost tile for
preselections made by the preselect-direction command.
A container with the id "PRESELECT" will be added to the workspace, and
replaced when the next manage-able window is spawned.
A new command, cancel-preselect, has been added to remove both the
preselection index and the ghost tile if the user changes their mind.
This commit adds a new feature to preselect the direction of the next
spawned window with a corresponding komorebic preselect-direction
command which takes an OperationDirection.
If the OperationDirection is valid from the current position, it will be
stored in the Workspace state, and then read, applied, and deleted when
the next manage-able window is spawned.
Direction preselection does not (yet?) support the Grid layout.
Marking all --ahk flags in the start/stop/kill/enable-autostart commands
as EOL; there are any number of ways that users can manage their own
launching of AutoHotKey scripts and the complexity of the different ways
that AHK can be installed is not worth the maintenance burden for this
project.
This commit adds a new LayoutOptions option, GridLayoutOptions,
currently with a single configurable "rows" opt which can be use to
constrain the grid by number of rows.
This commit adds a new option under layout_options.scrolling -
"center_focused_column", which defaults to false. When
set to true, and when the number of scrolling columns is an odd number
>=3, komorebi will, if there are enough windows being managed on the
workspace, and if the focused window is not too close to either the
beginning or the end of the workspace ring, keep the focused window in a
centered position in the layout
This commit standardizes the codebase to disallow usage of the raw eyre!
macro for creating errors, instead using ok_or_eyre() when constructing
ad-hoc errors from Result and Option types, and otherwise using the
bail! macro in response to failed boolean conditions.
Following discussing in #1475, and considering the default of the
komorebi widget in komorebi-bar, this commit updates the default
StackbarLabel from Process to Title.
resolve#1475
This commit ensures that unstack_all is called on a workspace before
attempting to proceed with stack_all to avoid stacking loops that can
occur when there are multiple pre-existing stacks already created on the
workspace.
This commit ensures that the WM_SETCURSOR message is handled by stackbar
windows by setting the cursor to the default IDC_ARROW, which prevents
the spinning "Loading" icon from showing on hover.
re #1456
This commit adds a new DefaultLayout::Scrolling variant, along with a
new LayoutOptions configuration which will initially be used to allow
the user to declaratively specify the number of visible columns for the
Scrolling layout, and a new komorebic "scrolling-layout-columns" command
to allow the user to modify this value for the focused workspace at
runtime.
The Scrolling layout is inspired by the Niri scrolling window manager,
presenting a workspace as an infinite scrollable horizontal strip with a
viewport which includes the focused window + N other windows in columns.
There is no support for splitting columns into multiple rows.
This layout can currently only be applied to single-monitor setups as
the scrolling would result in layout calculations which push the windows
in the columns moving out of the viewport onto adjacent monitors.
This implementation in the current state is enough to be useable for me
personally, but if others want to iterate on this, make it handle
hiding/restoring windows correctly when scrolling the viewport so that
adjacent monitors don't get impacted etc., patches are always welcome.
resolve#1434
This commit ensures that the user can't get into a weird state by
attempting to float a monocle container. If a user attempts to call
toggle-float on a monocle container, a warning will be logged and the
command will be ignored.
This commit adds a new StateQuery variant, FocusedContainerKind, which
will will return None if there is not focused container on the current
workspace (ie. it is empty), Single if the focused container contains a
single window, or Stack if the focused container contains more than one
window.
This commit adds a simple egui helper application which shows a list of
shortcuts defined in a user's whkdrc file. Parsing AHK files is not
supported.
In addition to listing out shortcuts defined in the whkdrc file, the top
line allows users to add filter a filter to narrow down the list of
commands and key bindings to the ones they are interested in.
A new komorebic command "toggle-shortcuts" has been introduced which
will first attempt to kill "komorebi-shortcuts.exe", and then exit if
the kill signal was successful (ie. a process was closed), or proceed to
open "komorebi-shortcuts.exe" if the kill signal was not successful (ie.
no process was closed, so we should open one).
"komorebi-shortcuts.exe" has been added as a floating application in
lib.rs to allow for users to use the "komorebic move" command to
manipulate its position via their existing keyboard bindings.
This commit ensures that if a user sends one of the various messages
which ultimately call move_container_to_monitor, if the target workspace
on the target monitor currently has a monocle container, it will be
toggled off to allow space for the container being moved to be rendered.
This commit ensures that if a user sends one of the various messages
which ultimately call move_container_to_workspace, if the target
workspace currently has a monocle container, it will be toggled off to
allow space for the container being moved to be rendered.
This startup Win32 API call can sporadically fail, so this commit adds
some retry logic and logging of errors every time it fails. If it
crosses the retry threshold, the application will exit (because you
can't really have a tiling window manager running that doesn't let you
set the foreground window).
This commit ensures that workspace windows that are hidden when the user
is using HidingBehaviour::Hide will not be unintentionally reaped by a
hard-coded workaround for Microsoft Office's continuing
enshittification.
HidingBehaviour::Hide has also been marked in the docs as an EOL
feature, and some dead code that was ultimately migrated to reaper.rs
has been cleaned up.
fix#1426
This commit adds a new VirtualDesktopNotification which is used to
notify subscribers when the user leaves and enters the virtual desktop
associated with komorebi.
komorebi-bar consumes these notifications to minmize and restore the bar
appropriately depending on the currently focused virtual desktop.
re #1420
This commit improves path handling for commands and icons in the new
Application widget by making use of PathExt::replace_env when loading
the user-specified ApplicationsConfig.
Crucially for scoop users, this means that user-agnostic references to
scoop apps can now be made like this:
```
$Env:USERPROFILE/scoop/apps/zed-nightly/current/zed.exe
```
When attempting to look up an icon for a command, we now split the
command on ".exe", and if this is a complete path to a file, we try to
use it to extract an icon, otherwise we try to resolve a complete path
using "which" before doing the same.
This commit ensures that all borders will destroyed if komorebi detects
that the user has switched to a Windows Virtual Desktop different from
the one it was launched on and associated with.
There is still some work to be done to forcibly redraw the borders when
a navigation back to the VD associated with komorebi is detected, but
that requires tracking the state of the current VD ID outside of the
process_event handler somewhere.
re #1420
This commit adds a dedicated WindowHandlingBehaviour enum with Sync and
Async variants, and reverts a change to the render fn in window.rs to
use move_window instead of position_window if the
WindowHandlingBehaviour variant is Sync.
This commit adds an option to set the floating layer behaviour for all
workspaces on a monitor.
Overrides set on an individual workspace level will take precedence over
the option set at the monitor level.
This commit removes RetileWithResizeDimensions messages from the batches
that are sent to komorebi when changing workspace.
I'm not really sure why this was added in the first place, but removing
it doesn't seem to impact layouts on workspace switch, and more
important, by removing this message I'm no longer able to reproduce the
sudden exits of komorebi.exe under sustained workspace switching calls
made via the bar.
This commit addresses a deadlock on WINDOW_BORDERS which can occur under
load, particularly when switching workspaces.
When switching workspaces using the komorebi-bar,
RetileWithResizeDimensions is also called, which calls
border_manager::destroy_all_borders.
At the same time, border_manager::window_border is called when hiding
borders from the previous workspace. Both of these functions try to take
locks on WINDOWS_BORDERS and BORDER_STATE.
border_manager::window_border is called in a new thread by hide_border,
so if this has not finished with the lock by the time
destroy_all_borders requests it, we get stuck in a deadlock.
The changes in this commit ensure that the BORDER_STATE lock is dropped
as early as possible in destroy_all_borders, and that we are not holding
the WINDOWS_BORDERS lock in window_border while trying to get the
BORDER_STATE lock to look up BorderInfo.
The logs which show the initial deadlock being detected:
2025-04-08T22:49:37.641888Z WARN komorebi::process_command: could not acquire window manager lock, not processing message: FocusWorkspaceNumber
2025-04-08T22:49:38.294952Z WARN komorebi::process_command: could not acquire window manager lock, not processing message: FocusWorkspaceNumber
2025-04-08T22:49:39.225645Z ERROR komorebi: 1 deadlocks detected
2025-04-08T22:49:39.225826Z ERROR komorebi: deadlock #0
2025-04-08T22:49:39.225950Z ERROR komorebi: thread id: 9896
2025-04-08T22:49:39.226072Z ERROR komorebi: 0: 0x7ff68fd33dec - backtrace::backtrace::dbghelp64::trace
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\dbghelp64.rs:99
backtrace::backtrace::trace_unsynchronized<backtrace::capture::impl$1::create::closure_env$0>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\mod.rs:66
1: 0x7ff68fd33c87 - backtrace::backtrace::trace<backtrace::capture::impl$1::create::closure_env$0>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\mod.rs:53
2: 0x7ff68fd3c03e - backtrace::capture::Backtrace::create
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\capture.rs:193
3: 0x7ff68fd3bfae - backtrace::capture::Backtrace::new
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\capture.rs:158
4: 0x7ff68f937b54 - parking_lot_core::parking_lot::deadlock_impl::on_unpark
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:1211
5: 0x7ff68f921f0e - parking_lot_core::parking_lot::deadlock::on_unpark
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:1144
6: 0x7ff68f92bf88 - parking_lot_core::parking_lot::park::closure$0<parking_lot::raw_mutex::impl$3::lock_slow::closure_env$0,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$1,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$2>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:637
7: 0x7ff68f92a777 - parking_lot_core::parking_lot::with_thread_data
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:207
parking_lot_core::parking_lot::park<parking_lot::raw_mutex::impl$3::lock_slow::closure_env$0,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$1,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$2>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:600
8: 0x7ff68f926fa0 - parking_lot::raw_mutex::RawMutex::lock_slow
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot-0.12.3\src\raw_mutex.rs:262
9: 0x7ff68f1daac6 - parking_lot::raw_mutex::impl$0::lock
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot-0.12.3\src\raw_mutex.rs:72
10: 0x7ff68ef05eb3 - lock_api::mutex::Mutex<parking_lot::raw_mutex::RawMutex,std::collections::hash::map::HashMap<isize,alloc::string::String,std::hash::random::RandomState> >::lock<parking_lot::raw_mutex::RawMutex,std::collections::hash::map::HashMap<isize,alloc::string::Str
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\lock_api-0.4.12\src\mutex.rs:223
11: 0x7ff68ef0713d - komorebi::border_manager::destroy_all_borders
at komorebi\src\border_manager\mod.rs:139
12: 0x7ff68f194e61 - komorebi::window_manager::WindowManager::process_command<ref_mut$<uds_windows::stdnet::net::UnixStream> >
at komorebi\src\process_command.rs:918
13: 0x7ff68ef1fdcf - komorebi::process_command::read_commands_uds
at komorebi\src\process_command.rs:2292
14: 0x7ff68f231eb5 - komorebi::process_command::listen_for_commands::closure$0::closure$0::closure$0
at komorebi\src\process_command.rs:123
15: 0x7ff68f32bcf3 - core::hint::black_box
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\hint.rs:477
std::sys::backtrace::__rust_begin_short_backtrace<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\sys\backtrace.rs:152
16: 0x7ff68f00d1b4 - std::thread::impl$0::spawn_unchecked_::closure$1::closure$0<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\thread\mod.rs:559
17: 0x7ff68f356ea1 - core::panic::unwind_safe::impl$25::call_once<tuple$<>,std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> > >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\panic\unwind_safe.rs:272
18: 0x7ff68f1d1e00 - std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::impl$0::spawn_unchecked_::closure$1::closure_env$0<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> > >,tuple$<> >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:587
19: 0x7ff68f0102b3 - std::thread::impl$7::drop::closure$0<enum2$<core::result::Result<tuple$<>,eyre::Report> > >
20: 0x7ff68f007be7 - std::panicking::try
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panicking.rs:550
std::panic::catch_unwind
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\panic.rs:358
std::thread::impl$0::spawn_unchecked_::closure$1<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\std\src\thread\mod.rs:557
21: 0x7ff68ecfeb8e - core::ops::function::FnOnce::call_once<std::thread::impl$0::spawn_unchecked_::closure_env$1<komorebi::process_command::listen_for_commands::closure$0::closure$0::closure_env$0,tuple$<> >,tuple$<> >
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250
22: 0x7ff68fe84f1d - alloc::boxed::impl$28::call_once
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library\alloc\src\boxed.rs:1976
alloc::boxed::impl$28::call_once
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library\alloc\src\boxed.rs:1976
std::sys::pal::windows::thread::impl$0::new::thread_start
at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library\std\src\sys\pal\windows\thread.rs:56
23: 0x7ff8ef27257d - BaseThreadInitThunk
24: 0x7ff8f042af28 - RtlUserThreadStart
2025-04-08T22:49:39.228617Z ERROR komorebi: thread id: 62364
2025-04-08T22:49:39.228731Z ERROR komorebi: 0: 0x7ff68fd33dec - backtrace::backtrace::dbghelp64::trace
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\dbghelp64.rs:99
backtrace::backtrace::trace_unsynchronized<backtrace::capture::impl$1::create::closure_env$0>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\mod.rs:66
1: 0x7ff68fd33c87 - backtrace::backtrace::trace<backtrace::capture::impl$1::create::closure_env$0>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\backtrace\mod.rs:53
2: 0x7ff68fd3c03e - backtrace::capture::Backtrace::create
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\capture.rs:193
3: 0x7ff68fd3bfae - backtrace::capture::Backtrace::new
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\backtrace-0.3.71\src\capture.rs:158
4: 0x7ff68f937b54 - parking_lot_core::parking_lot::deadlock_impl::on_unpark
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:1211
5: 0x7ff68f921f0e - parking_lot_core::parking_lot::deadlock::on_unpark
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:1144
6: 0x7ff68f92bf88 - parking_lot_core::parking_lot::park::closure$0<parking_lot::raw_mutex::impl$3::lock_slow::closure_env$0,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$1,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$2>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:637
7: 0x7ff68f92a777 - parking_lot_core::parking_lot::with_thread_data
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:207
parking_lot_core::parking_lot::park<parking_lot::raw_mutex::impl$3::lock_slow::closure_env$0,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$1,parking_lot::raw_mutex::impl$3::lock_slow::closure_env$2>
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot_core-0.9.10\src\parking_lot.rs:600
8: 0x7ff68f926fa0 - parking_lot::raw_mutex::RawMutex::lock_slow
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot-0.12.3\src\raw_mutex.rs:262
9: 0x7ff68f1daac6 - parking_lot::raw_mutex::impl$0::lock
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\parking_lot-0.12.3\src\raw_mutex.rs:72
10: 0x7ff68ef05d93 - lock_api::mutex::Mutex<parking_lot::raw_mutex::RawMutex,std::collections::hash::map::HashMap<alloc::string::String,alloc::boxed::Box<komorebi::border_manager::border::Border,alloc::alloc::Global>,std::hash::random::RandomState> >::lock<parking_lot::raw_mu
at C:\Users\LGUG2Z\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\lock_api-0.4.12\src\mutex.rs:223
11: 0x7ff68f2064de - komorebi::border_manager::window_border::closure$0
at komorebi\src\border_manager\mod.rs:109
12: 0x7ff68eddeca9 - enum2$<core::option::Option<ref$<alloc::string::String> > >::and_then<ref$<alloc::string::String>,komorebi::border_manager::BorderInfo,komorebi::border_manager::window_border::closure_env$0>
at C:\Users\LGUG2Z\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\option.rs:1452
13: 0x7ff68ef06339 - komorebi::border_manager::window_border
at komorebi\src\border_manager\mod.rs:108
We shouldn't ever have empty containers, but never say never because
someone on the Discord has an empty container with no Windows that
continues to take up a tile. This commit adds a call to drop all
containers without any windows whenever Workspace::update is called.