1573 Commits

Author SHA1 Message Date
LGUG2Z
6ca49d4301 chore(deps): cargo update 2026-02-20 16:58:40 -08:00
Csaba
634a3e7f3b fix(wm): correct bsp/grid container positioning when flipped
The recursive_fibonacci function used formulas for main_x and main_y
that mixed unresized area dimensions with resized dimensions when
calculating flipped positions.

This caused a gap between containers proportional to resize_delta * (2 *
ratio - 1), meaning any column_ratios or row_ratios value other than the
default 0.5 would produce visible gaps or overlaps between containers
when the layout was both flipped and resized.

The fix replaces the flipped position formulas with ones that derive
main_x and main_y directly from the alt area width and height
expressions used by the recursive child call, ensuring the main
container is always positioned immediately adjacent to the alt area
regardless of ratio or resize delta.

The horizontal flip for grid layouts swapped column left positions
directly from the unflipped layout. With non-default column ratios this
caused narrow columns to receive wide column positions, producing
overlapping containers.

Precompute flipped column left positions by laying out the original
column widths in reverse order from the area left edge, ensuring
containers tile without overlap regardless of column ratio.

Separated all unit test for arrangements into a new file.

Added 4 BSP-specific regression tests (horizontal flip, vertical flip,
both axes, sweep across multiple ratios/deltas).

Added 7 adjacency tests covering ALL other layouts (Columns, Rows,
VerticalStack, RightMainVerticalStack, HorizontalStack,
UltrawideVerticalStack, Scrolling) confirming they don't have the gap
issue.

Added 2 Grid tests that verify containers don't overlap and tile the
full area when column ratios are non-default. The first test checks a
horizontal flip specifically, confirming 2 columns form with no gaps
edge-to-edge. The second test runs all three flip axes and asserts no
overlaps and full area coverage for each.
2026-02-20 16:57:21 -08:00
LGUG2Z
5b6fab0044 chore(dev): begin v0.1.41-dev 2026-02-20 16:56:51 -08:00
LGUG2Z
bed314b866 chore(release): v0.1.40 v0.1.40 2026-02-14 12:52:25 -08:00
Csaba
c165172b5a feat(cli): new layout-ratios command
The implementation adds a new layout-ratio CLI command to komorebi that
allows users to dynamically set column and row ratios for layouts at
runtime via komorebic layout-ratio --columns 0.3 0.4 --rows 0.5.

A public validate_ratios function was added to komorebi-layouts that
clamps ratio values between 0.1 and 0.9, truncates when the cumulative
sum would reach or exceed 1.0, and limits to a maximum of 5 ratios. This
function is shared between config file deserialization and the new CLI
command, ensuring consistent validation behavior.

The SocketMessage enum in komorebi/src/core/mod.rs has a new
LayoutRatios variant. The handler in process_command.rs uses the shared
validate_ratios function to process the ratios before applying them to
the focused workspace's layout options.

When the CLI command is called without any arguments, it prints a
helpful message instead of returning an error.
2026-02-13 11:24:57 -08:00
Csaba
9977cca500 feat(bar): enhancing the media widget
The Media widget has been enhanced with a new MediaDisplayFormat
configuration option that controls how the widget is displayed. It
supports seven formats: Icon, Text, IconAndText (the default),
ControlsOnly, IconAndControls, TextAndControls, and Full.

The widget now detects whether the Previous and Next buttons are
actually available for the current media session using the Windows Media
Control API. When a button is not available, it appears dimmed at 50
percent opacity and clicking it has no effect.

Tooltips were added to improve usability. Hovering over the media info
label shows the full artist and title text, which is helpful when the
text is truncated. The Play/Pause button also shows the media info on
hover.

The rendering logic was refactored to properly handle right-aligned
widgets.

When the Media widget is placed in right_widgets, the UI renders items
from right to left, so the code now renders elements in reverse order to
ensure the visual appearance remains consistent regardless of which
panel the widget is placed in.
2026-02-12 20:39:16 -08:00
LGUG2Z
5d7a0ea9ad chore(deps): cargo update 2026-02-12 20:35:29 -08:00
dependabot[bot]
0e79c58be3 chore(deps): bump actions/download-artifact from 6 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 15:46:39 -08:00
dependabot[bot]
09205bfd83 chore(deps): bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-07 15:46:31 -08:00
Rejdukien
98122bd9d4 fix(wm): prevent window-removal race after display changes
This prevents a race where OS-initiated minimizes prematurely
remove windows that should be transiently restored.

Problem: a display-change can trigger a a fast reconciliation path (same
count), and shortly after Windows may emit a SystemMinimizeStart for
affected windows.  The minimize handler treated those as user-initiated
and removed the window, making later reconciliation unable to restore
it.

Fix: timestamp display-change notifications and add a
display_change_in_progress(period) check to the minimize handler.  While
that grace period is active the minimize handler skips remove_window(),
preserving windows so the reconciliator can restore them.
2026-02-07 15:28:46 -08:00
Rejdukien
9741b387a7 fix(wm): add verification step in monitor reconciliator to handle noisy Win32 events
This change adds verification steps and robustness improvements
to the monitor reconciliator.

- Retry display enumeration if it fails, up to 5 times, instead of silently
  ignoring errors.
- When a potential monitor removal is detected, drop locks and wait briefly,
  then re-query the Win32 display API. If the monitor reappears, treat the
  event as transient and do not remove the monitor.
- Re-acquire locks when needed, so the verification wait doesn't hold
  internal state locks.

Why:
- Win32 device/display notifications are noisy and not always immediately
  available; enumeration can fail (e.g. "Invalid monitor handle" / HRESULT
  0x80070585), or multiple DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE events
  can arrive in quick succession.
- Without verification the reconciliator could treat transient events (for
  example, power-plan–induced monitor sleep where Removes+Adds occur within
  milliseconds) as real removals, resulting in fewer detected monitors than
  are actually present.
2026-02-07 15:28:40 -08:00
LGUG2Z
1dad13106a refactor(layouts): add darwin feature gate and expand win32 feature gate
This commit builds on the newly introduced komorebi-layouts crate to
make it suitable for wholesale adoption in komorebi for Mac.
2026-02-07 15:28:40 -08:00
LGUG2Z
0b5141e7a4 refactor(layouts): extract independent komorebi-layouts crate
This commit moves layout-related code into a new workspace crate
komorebi-layouts, with the intention of re-using it all in komorebi for
Mac instead of maintaining two separate implementations.
2026-02-07 15:28:40 -08:00
Csaba
22e8a79833 feat(wm): add layout_options with ratios support
Added customizable split ratios for layouts via layout_options
configuration. Users can now specify column_ratios and row_ratios arrays
to control window sizing in various layouts.

Ratios are validated at config load time: values are clamped between 0.1
and 0.9 to prevent zero-sized windows, and arrays are automatically
truncated when their cumulative sum would reach or exceed 1.0. This
ensures there's always remaining space for additional windows.

Ratio support varies by layout:

- Columns and Rows layouts use the full arrays for each column/row width
  or height
- VerticalStack, RightMainVerticalStack, and HorizontalStack use the
  first ratio for the primary split and the remaining ratios for stack
  windows
- BSP uses the first value from each array for horizontal and vertical
  splits respectively
- Grid only supports column_ratios since row counts vary dynamically.
- UltrawideVerticalStack uses the first two column ratios for center and
  left columns.

All ratio-related values are now defined as constants in
default_layout.rs: MAX_RATIOS (5), MIN_RATIO (0.1), MAX_RATIO (0.9),
DEFAULT_RATIO (0.5), and DEFAULT_SECONDARY_RATIO (0.25 for
UltrawideVerticalStack).
2026-02-07 15:27:48 -08:00
LGUG2Z
5946caaf92 docs(quickstart): enable fewer widgets in the bar's example config 2026-02-06 17:06:36 -08:00
LGUG2Z
01d73b7d19 fix(borders): prevent use-after-free take 3
This commit attempts tofixfixes a use-after-free bug in the
border_manager that was causing crashes with exception code 0xc000041d
(FATAL_USER_CALLBACK_EXCEPTION) when borders were being destroyed during
config updates.

The root cause was a race condition between the main thread destroying a
border window and the border's window thread still processing queued
window messages (EVENT_OBJECT_LOCATIONCHANGE, WM_PAINT).

The crash occurred when these callbacks invoked render_target.EndDraw(),
which internally calls HwndPresenter::Present() to present the rendered
frame to the HWND. By this point, the HWND and its associated Direct2D
surfaces had been freed by WM_DESTROY, resulting in Direct2D attempting
to dereference freed memory (0xbaadf00dbaadf00d - debug heap poison
value).

The previous attempts at fixing this issue (bdef1448, dbde351e)
addressed symptoms but not the fundamental race condition. bdef1448
attempted to release resources on the main thread before destruction,
but this created a cross-thread race. dbde351e moved resource cleanup to
WM_DESTROY, but this still allowed EVENT_OBJECT_LOCATIONCHANGE/WM_PAINT
handlers to check `render_target.is_some()`, context-switch to
WM_DESTROY which clears it, then context-switch back and call EndDraw()
on a now-invalid reference.

This commit attempts to eliminate the race condition by introducing an
atomic destruction flag that serves as a memory barrier between the
destruction path and the rendering paths:

- Added `is_destroying: Arc<AtomicBool>` field to the Border struct
- In destroy(): Sets the flag with Release ordering, sleeps 10ms to
  allow in-flight operations to complete, then proceeds with cleanup
- In EVENT_OBJECT_LOCATIONCHANGE and WM_PAINT: Checks the flag with
  Acquire ordering both at handler entry and immediately before calling
  BeginDraw/EndDraw, exiting early if destruction is in progress

The Acquire/Release memory ordering creates a synchronizes-with
relationship that ensures:

1. When the destruction flag is set, all subsequent handler checks will
   see it (no stale cached values)
2. Handlers that pass the first check but race with destruction will be
   caught by the second check before touching D2D resources
3. The 10ms sleep window allows any handler already past both checks to
   complete its EndDraw() before resources are freed

This is a lock-free solution with zero overhead on the hot rendering
path (atomic loads are nearly free) and provides defense-in-depth with
multiple barriers against the use-after-free condition.
2026-02-06 16:57:06 -08:00
LGUG2Z
9d16197825 chore(deps): cargo update 2026-02-06 16:57:06 -08:00
LGUG2Z
fed09689b8 chore(deps): cargo update 2026-02-04 08:43:43 -08:00
LGUG2Z
c0b298c9de docs(readme): add link to komorebi for mac repo 2026-01-30 20:18:57 -08:00
LGUG2Z
5fd7017b71 fix(wm): prevent dupes when enforcing ws rules
This commit ensures that we check if a window is already managed in any
workspaces even after checking known_hwnds, because windows moved as
part of the earlier ensure_workspace_rules call can slip through the
cracks.
2026-01-30 20:18:57 -08:00
Rejdukien
dbde351e22 fix(border): release resources before destroying window to prevent access violations
When a border is destroyed, the main thread was forcefully releasing D2D
resources, while the border window thread might still be trying to use
them in its message loop.

Releasing the RenderTarget on one thread while another is calling
EndDraw on it leads to a use-after-free scenario or invalid state for
the COM object, resulting in the crash.

This commit applies a fix that moves the resource cleanup logic from the
main thread to the window thread. Now, resources are only released
during the WM_DESTROY message processing, which guarantees
synchronization with other window messages like WM_PAINT.
2026-01-30 20:18:43 -08:00
LGUG2Z
95838fc896 build(just): add target for running procdump nightly 2026-01-13 08:26:23 -08:00
LGUG2Z
bdef1448e1 fix(borders): prevent use-after-free in border destruction
This commit fixes a use-after-free bug in the border_manager that was
causing crashes with exception code 0xc000041d when borders were being
destroyed during workspace/monitor changes.

The root cause was a race condition between the main thread destroying a
border window and the border's window thread still processing queued
window messages (WM_PAINT, EVENT_OBJECT_LOCATIONCHANGE). The crash
occurred when these callbacks invoked render_target.EndDraw(), which
internally calls HwndPresenter::Present() to present the rendered frame
to the HWND. By this point, the HWND and its associated DirectX surfaces
had already been freed, resulting in Direct2D attempting to dereference
freed memory (0xFEEEFEEEFEEEFEEE - Windows heap poison value).

The issue stemmed from two problems in destroy_border():

1. Direct2D resources (render_target and brushes) were not being
   released before closing the window. These COM objects hold internal
   pointers to the HWND and its DirectX swap chain/surfaces. When
   close_window() was called, Windows began tearing down these resources
   while the Direct2D objects still held dangling pointers to them.

2. GWLP_USERDATA was not being cleared before closing the window. This
   meant that any messages already queued in the window's message queue
   could still retrieve the border_pointer and attempt to render using
   the now-invalid Direct2D resources.

This commit addresses both issues:

- In destroy_border() (mod.rs): Explicitly set render_target to None and
  clear the brushes HashMap before calling destroy(). This ensures that
  Direct2D COM objects are properly released while the HWND is still
  valid, preventing EndDraw() from accessing freed HWND resources.

- In destroy() (border.rs): Clear GWLP_USERDATA before calling
  close_window(). This ensures that any pending window messages will see
  a null pointer and exit early from the callbacks (which already have
  null pointer checks in place).

These changes create two layers of defense against the race condition:
the callbacks won't access the border_pointer (it's null), and even if
they somehow did, the render_target would be None so no rendering
operations would occur.

I think this is the root cause of a lot of crash tickets that people are
mistakenly attributing to specific applications which lack
reproducibility across different users/machines, i.e. #1626, #1624.
2026-01-13 08:26:23 -08:00
Rejdukien
0b39551470 fix(wm): clear last error before calling GetWindowLongPtrW
Apparently there is a quirk of GetWindowLongPtr when querying styles. If
the style bits are genuinely 0, the API returns 0 but does not clear the
last error.

If a previous API call set an error, GetWindowLongPtr might define
"failure" as "return 0 AND GetLastError != 0".

Hence, it is important to clear the last/previous error before calling
GetWindowLongPtrW to ensure that we don't misinterpret a valid 0 return
value as an error.
2026-01-13 08:26:23 -08:00
LGUG2Z
ab528f799d chore(deps): cargo update 2026-01-13 08:26:22 -08:00
LGUG2Z
8805fafa99 feat(bar): make explicit monitor config optional
This commit makes the monitor configuration option for the komorebi-bar
optional, defaulting to index 0.
2026-01-04 19:15:24 -08:00
LGUG2Z
1219c3d118 refactor(bar): inline defaults in schemars attrs 2026-01-04 19:08:41 -08:00
LGUG2Z
b792328676 fix(splash): show mdm workspace tenant name instead of mdm url
The WorkspaceTenantName is populated far more consistently than MdmUrl.

This commit switches to extracting that instead and passing it on to the
MDM splash screen so that users on non-corporate devices who may have
unintentionally enrolled themselves into BYOD MDM by logging into an
account a clicking "Yes" on some dark pattern pop-up have a clear
indication of why they are seeing the splash, and can take the
appropriate steps to remove the MDM profile from their system if
desired.
2026-01-04 07:33:32 -08:00
LGUG2Z
a7380db47c docs(readme): add debugging instructions for people who don't know they've been mdm'd 2026-01-04 07:15:59 -08:00
LGUG2Z
086993a7c0 fix(cli): respect --bar flag in start even when no config is resolved
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.
2026-01-03 16:55:59 -08:00
LGUG2Z
51e1337f40 fix(wm): consider window count when calculating scrolling column width
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.
2026-01-01 13:07:18 -08:00
LGUG2Z
6cef8d9ef6 refactor(schema): simplify defaults in static_config 2025-12-31 13:04:34 -08:00
LGUG2Z
6e36b81669 build(nix): add flake.nix for cross-compilation from darwin and linux
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.
2025-12-28 17:24:41 -08:00
LGUG2Z
0758c7d900 refactor(theme): centralize theme-related code in komorebi-themes
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.
2025-12-27 21:58:39 -08:00
LGUG2Z
f77a303b30 docs(schema): ensure every enum variant has a renderable title 2025-12-27 20:34:31 -08:00
LGUG2Z
3a81f7babb docs(schema): ensure all public-facing bar config opts have docstrings 2025-12-27 13:40:45 -08:00
LGUG2Z
3d8778a7d6 docs(schema): ensure all public-facing static config opts have docstrings 2025-12-27 12:58:46 -08:00
LGUG2Z
a42e809ade docs(config): encode defaults of unwrapped options into schema
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.
2025-12-26 18:16:55 -08:00
LGUG2Z
66c5766848 feat(cli): add custom output fir for docgen cmd 2025-12-26 10:26:00 -08:00
LGUG2Z
ac56590791 chore(clippy): address warnings 2025-12-23 17:19:00 -08:00
LGUG2Z
4d67f5fed3 build(just): update schemagen and add schemapub 2025-12-22 20:45:44 -08:00
LGUG2Z
c91224295f docs(schema): avoid duplicating display format enums in bar 2025-12-22 19:30:29 -08:00
LGUG2Z
4b1e3bd448 docs(schema): add deprecations and improve formatting 2025-12-21 21:56:17 -08:00
LGUG2Z
1cb8ed7f10 chore(deps): bump schemars to 1.1 2025-12-21 16:26:25 -08:00
LGUG2Z
90271f1f99 docs(schema): clarify that 'theme' take priority over 'border_colours' 2025-12-20 21:12:56 -08:00
LGUG2Z
9fde9ed6f8 chore(deps): cargo update 2025-12-20 11:40:31 -08:00
LGUG2Z
ce4479eb9f chore(deps): cargo update 2025-12-16 18:05:51 -08:00
LGUG2Z
f3b7f5ac42 chore(clippy): odd unused_assignment failures in rust 1.92.0 2025-12-14 20:00:18 -08:00
LGUG2Z
382896dfd2 docs(readme): add detailed instructions for students w/ mdm devices 2025-12-14 14:16:48 -08:00
LGUG2Z
ff4187aecf chore(dev): begin v0.1.40-dev 2025-12-14 14:07:44 -08:00