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 makes it possible to send commands from the bar by using the
mouse/touchpad/touchscreen.
Komorebi or custom commands can be sent by clicking on the mouse's
primary, secondary, middle, back or forward buttons.
As the primary single click is already used by widgets, only primary
double clicks can send commands. This limitation is due to Egui also
triggering 2 single clicks before a double click is triggered. Egui does
not have an implementation for stopping event propagation out of the box
and would be too much work to include.
Similarly, commands can be sent on every "tick" of mouse scrolling,
touchpad or touchscreen swiping in any of the 4 directions. This "tick"
can be adjusted to fit user's preference.
This is due to the fact, that Egui does not have an event for when a
mouse "tick" occurs. It instead gives a number of points that the user
scrolled/swiped on each frame.
PR: #1403
Created a test for the transfer_window function.
The tests attempts to transfer a window to a monitor that doesn't exist,
and checks to see if we return an error. The test successfully gets an
error but there is a bug where the window isn't in the contiainer after
a failed transfer. I wrote a note comment to explain the bug just in
case we need to reference back to it.
Created a test for moving the focused workspace to a monitor that hasn't
been created. The test ensures that we receive an error when moving a
workspace to a monitor that doesn't exist.
Created a test for moving a window to a container that doesn't exist.
The test ensures that we receive an error when moving a window to a
container that doesn't exist.
Created a test for removing a window that doesn't exist.
The test creates a container with a window and then attempts to remove a
window that doesn't exist. The test will check the result to ensure that
the result returned an error and that we still have the expected number
of windows.
Created a test for moving a container to a workspace that doesn't exist.
The test ensures when a container is moved to a workspace that doesn't
exist, the workspace is created and that the workspace contains the
container.
It also checks that there are N workspaces available where N is the
largest workspace number.
Created a test for moving a workspace that doesn't exist. The test
attempts to remove a workspace that doesn't exist and checks to ensure
the removed_workspace variable is None.
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 pull request introduces a new Applications widget that displays a
user-defined list of application launchers in the UI. Each app entry
supports an icon, a label, and executes its configured command on click.
The design of this widget is inspired by the Applications Widget of YASB
Reborn. I personally missed this functionality and aimed to bring a
similar experience to komorebi-bar.
Further information is in the text of PR #1415
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.
Add configuration option for enabling/disabling the asynchronous window
handling. The feature might cause unforeseen issues, so by default it is
off. It can be enabled with setting the option async_window_handling to
true.
Unresponsive windows might cause Komorebi to hang because functions like
SetWindowPos wait for the target window's WindowProc.
I changed most SetWindowPos calls to use the SWP_ASYNCWINDOWPOS flag,
which should avoid hanging, and I changed the one
WindowsApi::move_window call to use position_window instead. I also
changed the ShowWindow to ShowWindowAsync.
The only issue I noticed was that it caused the stackbar to disappear
(and/or flicker), probably because the window is modified right after
moving it, so I disabled the async position_window for that.
Created a simple test for the
add_window_handle_to_move_based_on_workspace_rules function.
The test creates mock data representing window and the movement details.
The test will call the function with an empty vector to hold the
workspace rules and then check that the workspace rules are in the
vector.
Created a test for the ensure_named_workspace_for_monitor function.
The test creates two monitors and holds a list of workspace names.
When calling the ensure_named_workspace_for_monitor_function the test
checks to ensure that the the the monitor contains the length of the
list workspaces and that the workspaces uses the name in the list.
The test adds more names to the list and repeats the check on the other
monitor.
Created tests for the toggle_monocle and toggle_maximize functions. The
test are simialar to the maximize and unmaximize test and the monocle on
and off test.
Created a test for the monocle_on and monocle_off functions.
The test checks to ensure that the focused workspace container becomes a
monocle container when calling the monocle_on function.
The test will also ensure that the container is moved to the workspace's
container ring when the monocle_off function is called.
Created a test for maximizing and unmaximizing a window.
The test ensures that the when calling maximize_window, the focused
window is added to the maximized_window list.
When calling unmaximized window, the checks to ensure that None is
returned to ensure that the maximized window is removed from the list.
The test switches to a different window and performs the same checks.
This commit fixes the default for `CrossBoundaryBehaviour` which was set
to `Monitor` when loading a user configuration but was set to
`Workspace` if starting from fresh without a user config.
The docs also stated that this value was `Monitor` by default.
This commit adds a few more options to combine with the
`FloatingLayerBehaviour` which determine placement in different
situations:
- `"toggle_float_placement"`: the placement to be used by a floating
window when it is forced to float with the `toggle-float` command.
- `"floating_layer_placement"`: the placement to be used by a floating
window when it is spawned on the floating layer and the user has the
floating layer behaviour set to float.
- `"floating_override_placement"`: the placement to be used by a window
that is spawned when float override is active.
- `"float_rule_placement"`: the placement to be used by a window that
matches a 'floating_applications' rule.
Each `Placement` can be one of the following types:
- "None": windows are spawned wherever Windows positions them with
whatever size they had. Komorebi does not change its size or position.
- "Center": windows are centered without changing their size.
- "CenterAndResize": windows are centered and resized according to the
defined aspect ratio.
By default the placements are as follows:
- `"toggle_float_placement"`: `"CenterAndResize"`
- `"floating_layer_placement"`: `"Center"`
- `"floating_override_placement"`: `"None"`
- `"float_rule_placement"`: `"None"`
This commit also adds the `floating_layer_behaviour` as a global config.
When using the eager focus command to focus a window that was hidden on
a stack on an unfocused workspace it would load the workspace focus the
correct window but keep it cloaked with the previously focused window
showing. This would happen because we were focusing the workspace before
focusing the window which would create two window focus events, the
first one for the previously focused window and the second one for the
target window we wanted. The problem was that when the first event was
handled it would again refocus that window and cloak the target window
again so when the target window focus event came it would ignore it
since the `should_manage` function would return `false` because the
window was cloaked and had all its window styles as `None`, unless we
had transparency enabled, in that case it would override it and create a
focus loop.
This commit fixes this by not loading the newly focused workspace
immediately. Instead we first focus the workspace if it is needed
without loading it and mark it as needing to be loaded. Then we focus
the correct target window and only then do we load the workspace. This
way the workspace loads with the correct window already focused.
This is the same approach that is currently being done on the
`perform_reconciliation` function when there is an alt-tab.
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.
Created a test for the toggle_lock function. The test creates a
workspace with multiple containers and checks to see whether the
container was added or removed from the locked containers list when
calling the toggle_lock function.
Created a test for the float_window function. The test checks to ensure
that when calling the float_window function, the window is added to the
floating_windows list and removed from the containers window list. The
test will also check the count and ensure the expected window was added.
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.
This commit makes sure the border's width and offset is removed when you
disable the borders.
If you want to still have that size on your gaps when borders are
disabled then you should add it to the `default_container_padding` or to
the per-monitor or per-workspace `container_padding`.