Commit Graph
1629 Commits
Author SHA1 Message Date
LGUG2Z 3b93035d48 feat(scrolling): lay out the scrolling layout with the scrolling model
The Scrolling layout now keeps a ScrollingSpace per workspace, keyed by
container id, instead of recomputing equal-width columns from the previous
layout on every update. Column widths follow layout_options.scrolling.columns
and persist as windows come and go.
2026-10-30 15:45:37 -07:00
LGUG2Z 6c4eb621d3 feat(scrolling): add a platform-free model of a scrollable strip of columns
Ported from komorebi-for-mac (feature/moar-scrolling). The crate has no
dependencies and no knowledge of windows; it is not used by komorebi yet.
2026-10-30 15:45:37 -07:00
LGUG2Z a4e03e3d76 test(resize): cover the top edge of the lower tile of a BSP layout
Dragging the edge two stacked tiles share moves it by the same amount from
either tile, in the layout and in the window manager. A drag of the top edge
of the lower tile that moves nothing is not for want of this.
2026-10-30 15:45:37 -07:00
LGUG2Z dc9b2e9539 fix(window): uncloak the windows a killed daemon left cloaked
A daemon that is killed does not get to uncloak the windows it hid, and the
next one skips them as cloaked, so they stay out of sight and unmanaged.

A cloak by komorebi reads the same as one for another virtual desktop or a
suspended app, so keep a record of the windows that are cloaked and uncloak
those that the last daemon left behind, before looking for windows.
2026-10-30 15:45:37 -07:00
LGUG2Z 0b57006053 chore(deps): update libz-sys to 1.1.31 for RUSTSEC-2026-0418
libz-sys before 1.1.31 logs the compiler command of its zlib probe, which
with cc 1.6.0 or later includes every environment variable of the build,
and cargo stores that output under target/. It is a build dependency of
shadow-rs via git2 and libgit2-sys; bump it in the lockfile.
2026-10-30 12:13:42 -07:00
LGUG2Z 5c8e4770dc fix(monitor): do not mistake an unplugged display for one that is still attached
Displays were matched on serial_number_id.eq(..) || device_id.eq(..), so
two displays with no serial number matched each other through None == None,
and two with no device path through the shared "UNKNOWN" device id. When
a display without either was unplugged, the one that stayed attached
"matched" it, nothing was removed, and every display change started the
3s verification again; state kept listing the display for good.

A serial number now identifies a display only when both have one and it is
not empty, and a display without a device path is told apart by its name.
The removal takes the monitors out by index instead of by id, so that two
displays sharing the id UNKNOWN are not removed together, and adding a
connected monitor no longer skips a display for sharing that id.
2026-10-30 12:13:42 -07:00
LGUG2Z 3b424dd707 fix(config): accept an integer for an initial window placement rule
PlacementTarget deserialized through String, so the integer the docs
promise ("initial_window_placement_rules": 2) made the daemon exit at
start with an untagged enum error while "2" worked. Accept integers as
well as strings.

The derived schema described an externally tagged object that neither
form matches, so describe the type by hand and regenerate both schemas.
2026-10-30 12:13:42 -07:00
LGUG2Z 837ca257d4 fix(monitor): validate the target before moving a container or workspace
move_container_to_monitor and move_workspace_to_monitor took the container
or workspace out of its place and only then looked the target monitor up,
so a monitor index that does not exist left the window on screen but in no
workspace. Check the index first so the command fails and nothing moves.
2026-10-30 12:13:42 -07:00
LGUG2Z 9b9cad997c fix(rules): keep a rule whose id another kind of rule already has
The socket handlers for manage, ignore, object-name-change, tray, layered
and title bar rules skipped a new rule when any simple rule had the same
id, whatever its kind, so ignore-rule exe foo after ignore-rule class foo
was dropped. Share one helper that compares the kind as well.
2026-10-30 12:13:42 -07:00
LGUG2Z a599e9ecc1 chore: keep the line endings LF
Everything but dependencies.json is stored with LF, which nothing said, so
a CRLF file could get in unnoticed (it did, for two files, in a round trip
that shows as a rewrite of every line of both).

* .gitattributes: normalize every text file to LF in the repository and
  check it out with LF, whatever core.autocrlf is (the Windows runner
  defaults it to true)
* rustfmt.toml: newline_style = "Unix", so cargo fmt --check fails on
  a Rust file with CRLF
* CI: fail when a file is stored with CRLF
* dependencies.json: the one file that was stored with CRLF
2026-10-30 12:13:42 -07:00
LGUG2Z 21a821aca0 fix(nix): get the build working again 2026-10-28 19:30:00 -07:00
LGUG2Z efe91ce332 chore(schema): regenerate the schemas 2026-10-28 15:18:11 -07:00
LGUG2Z f08aa55511 chore(deps): cargo update 2026-10-27 09:59:57 -07:00
LGUG2Z a3fac99a0f fix(wm): make resizing a tile by dragging its edge reliable
Measure mouse resizes from the rect the window had when the drag
started, work in edges instead of guessing from rect fields, resize the
container of the dragged window, and bound resizes by the resulting tile
sizes (granting as much as fits) rather than by the size of the
container being resized.
2026-10-27 09:59:57 -07:00
LGUG2Z e0709f02bf chore(deps): cargo update 2026-08-22 14:26:23 -07:00
Csaba 83c4e11d99 feat(wm): add initial_window_placement_rules for workspace-level
Adds a new initial_window_placement_rules workspace configuration option
that allows users to control where new tiled windows are inserted in the
container list. Supports placement by named strategy (Primary,
Secondary, BeforeFocused, AfterFocused, Last), fixed container index, or
per-application matching rules with OR/AND logic.
2026-08-22 13:52:38 -07:00
Ransomwave 4ca2ff84dc feat(bar): add default interface options to display friendly name or local IP
Adds a new configuration to the Network widget from komorebi-bar, which
allows you to toggle between viewing your Default Interface's friendly
name, IPv4 or IPv6.

Motivation: I find it very useful to check your local IP at a glance,
and missed the feature heavily from other wms in Linux.
2026-08-22 13:52:01 -07:00
Dylan Anderson 3f3fac8b65 chore(stackbar): tighten deadlock fix comment 2026-08-02 20:24:18 -07:00
Dylan Anderson 5a534eb2ac fix(stackbar): drop containers lock before repositioning stackbar
Stackbar::update held STACKBARS_CONTAINERS across the synchronous
cross-thread SetWindowPos in position_window, while the stackbar wndproc
acquires the same lock on WM_LBUTTONDOWN. A tab click racing an update
storm leaves the wndproc blocked on the lock and the updater blocked in
SetWindowPos waiting on the wndproc's message pump, deadlocking the
whole process. Scope the guard to the insert so the lock is released
before SetWindowPos.

Fixes #1734
2026-08-02 20:24:18 -07:00
dependabot[bot] 1b263b9496 chore(deps): bump shadow-rs from 1.7.1 to 2.0.0
Bumps [shadow-rs](https://github.com/baoyachi/shadow-rs) from 1.7.1 to 2.0.0.
- [Release notes](https://github.com/baoyachi/shadow-rs/releases)
- [Commits](https://github.com/baoyachi/shadow-rs/compare/v1.7.1...v2.0.0)

---
updated-dependencies:
- dependency-name: shadow-rs
  dependency-version: 2.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-02 16:22:35 -07:00
dependabot[bot] e4e1b6da18 chore(deps): bump sysinfo from 0.38.4 to 0.39.6
Bumps [sysinfo](https://github.com/GuillaumeGomez/sysinfo) from 0.38.4 to 0.39.6.
- [Changelog](https://github.com/GuillaumeGomez/sysinfo/blob/main/CHANGELOG.md)
- [Commits](https://github.com/GuillaumeGomez/sysinfo/compare/v0.38.4...v0.39.6)

---
updated-dependencies:
- dependency-name: sysinfo
  dependency-version: 0.39.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-02 16:22:20 -07:00
Matt Kotsenas a10b8e27c1 fix(wm): honor layout_flip when focusing across a boundary
Cross-boundary focus arrival picked the structural leftmost/rightmost
container, ignoring layout_flip. Since layout_flip mirrors geometry
without reordering containers, focusing into a horizontally-flipped
workspace landed on the far edge instead of the container at the edge
the user crossed toward, while intra-workspace focus (via
OperationDirection::destination) already accounts for the flip.

Extract OperationDirection::cross_boundary_edge_index, which picks the
edge index against the flipped direction like destination() does, and
use it at the eight focus-arrival call sites. Add a komorebi-layouts
test that checks the pick against the rendered rectangles.
2026-07-22 14:15:07 -07:00
Matt Kotsenas 8a6868f2d2 fix(wm): refocus selection on destroy when foreground goes null
Reactive events pass trigger_focus=false so komorebi follows the OS
foreground and reconciles via the FocusChange handler once Windows
promotes a successor window. When the destroyed window was the
foreground and Windows promotes nothing, the foreground becomes null,
no FocusChange arrives, and komorebi's selection never regains OS focus,
leaving every border unfocused until the user refocuses by hand.

Add a narrow fallback in the Destroy/Unmanage handler: when the OS
foreground is null after removal, focus the current selection. Any
non-null foreground is left to FocusChange, so the OS is not fought and
the should_skip_focus_change desync cannot occur.
2026-07-22 14:14:56 -07:00
LGUG2Z fd3d52c477 chore(deps): cargo update 2026-07-21 10:53:14 -07:00
LGUG2Z d29069398b chore(cargo): address clippy warnings 2026-07-21 10:50:26 -07:00
dependabot[bot] ccb39abd2e chore(deps): bump starship-battery from 0.10.3 to 0.11.0
Bumps [starship-battery](https://github.com/starship/rust-battery) from 0.10.3 to 0.11.0.
- [Release notes](https://github.com/starship/rust-battery/releases)
- [Changelog](https://github.com/starship/rust-battery/blob/main/CHANGELOG.md)
- [Commits](https://github.com/starship/rust-battery/compare/v0.10.3...v0.11.0)

---
updated-dependencies:
- dependency-name: starship-battery
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 16:12:27 -07:00
dependabot[bot] f22fb8086c chore(deps): bump nanoid from 0.4.0 to 0.5.0
Bumps [nanoid](https://github.com/mrdimidium/nanoid) from 0.4.0 to 0.5.0.
- [Changelog](https://github.com/mrdimidium/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mrdimidium/nanoid/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: nanoid
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 16:11:37 -07:00
LGUG2Z 538053c9c8 chore(deps): cargo update 2026-06-24 14:58:47 -07:00
ChinhLee 57b93a7404 fix(wm): verify foreground state before mutating focus state
As a defense-in-depth fix independent of the event-conversion layer, the
WindowManagerEvent::FocusChange handler should not update the focused
monitor/workspace/container/window state when the incoming window is not
the actual OS foreground window.

The issue notes that after a monitor switch komorebi focuses "the last
window focused from monitor 1" while the popout window on the other
monitor still receives input.

This is exactly the symptom of komorebi processing a stale/background
focus event and re-pointing its selection at monitor 1 even though real
focus is elsewhere. Verifying foreground state before mutating focus
state prevents the desync regardless of which win-event produced the
FocusChange.

fix #1679

Signed-off-by: ChinhLee <76194645+chinhkrb113@users.noreply.github.com>
2026-06-24 14:55:43 -07:00
LGUG2Z a3b2e28dd5 feat(animations): set the ghost_movement default to false
There are a bunch of edge cases coming up with this behaviour. Setting
the default to false for now so people on the nightly can opt in as they
feel comfortable.
2026-05-04 19:09:28 -07:00
LGUG2Z 4e7d0e337c feat(wm): add monocle_focus_behaviour to gate monocle cycling
5f629e1f made directional focus cycle the monocle container in the
underlying ring. Useful on a single monitor, but undesired when focusing
across monitors — the focus stays trapped on the monocle workspace
instead of crossing the boundary.

This commit adds a new top-level monocle_focus_behaviour option with two
variants, Cycle (the post-5f629e1f behaviour) and NoOp (the pre-5f629e1f
behaviour, now the default), along with a ToggleMonocleFocusBehaviour
socket message and corresponding komorebic subcommands to flip between
them at runtime.
2026-05-04 19:09:27 -07:00
LGUG2Z e2e5dbfcae feat(wm): use ghost windows for movement animations
This commit tries to render move/resize animations on a DWM-thumbnail
"ghost" window instead of calling MoveWindow per-frame on the real HWND.

The source is cloaked via IApplicationView::SetCloak, the thumbnail is
animated via DwmUpdateThumbnailProperties on a layered host owned by a
single "ghost owner" thread, the border for the source follows the
lerped rect via a new WM_ANIMATE_RECT message handled on the border's
own WndProc thread (preserving today's per-frame border tracking), and
the real SetWindowPos happens once at the end of the animation.

Apps repaint exactly once per animation instead of N times, which is a
substantial win for heavy renderers (browsers, IDEs, Office). For
non-Chromium sources the source is also pre-positioned to target_rect
before the thumbnail is registered so the captured texture is target-
sized and downscales to native 1:1 at the end of the animation rather
than upscaling to a stretched/blurry final frame.

Chromium-shell sources  skip the pre-paint step: their
NativeWindowOcclusionTrackerWin reads DWMWA_CLOAKED and treats any cloak
value as hidden, suspending the renderer; WM_SIZE while cloaked produces
no new frame and the post-uncloak swap chain shows stale or black
content.

For those apps we keep the source cloaked at start_rect for the whole
animation and do the SetWindowPos in post_render after uncloak, where
the visibility flip is what wakes Viz back up.

A short ease-in opacity crossfade in post_render masks the texture
transition for the Chromium path and gives slow renderers time to
present their first post-resize frame before the overlay is removed.
2026-05-03 16:09:28 -07:00
LGUG2Z 937b28a7d9 chore(dev): begin 0.1.42-dev 2026-05-03 16:06:46 -07:00
Csaba 24c0ce0b1d feat(wm): add global layout_defaults for per-layout default options
Adds a top-level layout_defaults setting that defines default
layout_options and layout_options_rules per layout. Workspaces without
their own layout_options or layout_options_rules automatically inherit
the global defaults. If a workspace defines either setting, all global
defaults for that layout are fully replaced.
v0.1.41
2026-05-03 10:16:20 -07:00
Csaba d6b17bbc7c feat(wm): add threshold-based layout_options_rules for workspaces
Adds layout_options_rules to workspace configuration, allowing
layout_options to dynamically change based on container count. Uses the
same threshold semantics as layout_rules: when container count >=
threshold, the highest matching rule fully replaces the base
layout_options.
2026-05-03 10:16:18 -07:00
LGUG2Z e9a541d12b chore(deps): bump flavours 2026-05-01 17:10:20 -07:00
LGUG2Z 588d22a9d2 chore(cargo): address clippy warnings 2026-05-01 15:17:33 -07:00
LGUG2Z 53ad7a2224 chore(deps): cargo update 2026-05-01 14:59:04 -07:00
LGUG2Z 26b1464381 fix(borders): prevent use-after-free take 4
This commit fixes a cross-thread use-after-free crash with exception
code 0xc000041d (FATAL_USER_CALLBACK_EXCEPTION), identified via WinDbg
analysis of a minidump where rax=0xfeeefeeefeeefeee at the crash site in
d2d1!HwndPresenter::Present - the Windows heap freed-memory fill pattern
confirming Direct2D dereferenced a previously freed object.

The root cause was a data race between the border manager thread and the
border's own message loop thread. Border::create() spawns a dedicated
thread (Thread B) for the HWND message loop and sends Box<Border> back
to the border manager thread (Thread A) via a channel. After this point
both threads accessed Border::render_target concurrently without any
synchronisation:

Thread A called update_brushes() directly on the Box<Border>, replacing
render_target with a new ID2D1HwndRenderTarget and dropping the old one.
Dropping the old RenderTarget decremented the COM refcount to zero,
causing D2D to free its internal HwndPresenter.

Thread B was concurrently mid-render in a WM_PAINT or
EVENT_OBJECT_LOCATIONCHANGE handler, holding a reference to that same
old render target obtained via the GWLP_USERDATA raw pointer. Calling
EndDraw() after HwndPresenter was freed produced the crash. The process
uptime of two seconds in the dump confirmed this happened during startup
workspace initialisation, when a ForceUpdate notification triggered
update_brushes() while the newly-shown border window was processing its
first WM_PAINT.

The fix routes all brush update requests through the border's own
message loop by posting a custom WM_UPDATE_BRUSHES (WM_USER + 1) message
instead of calling update_brushes() cross-thread. The three call sites
in the border manager that previously called border.update_brushes()?
directly now call border.request_brush_update(), which posts the message
via PostMessageW. The WndProc handler for WM_UPDATE_BRUSHES calls
update_brushes() and invalidate() entirely on Thread B, eliminating the
race.

A secondary bug in destroy() was also fixed: it was clearing
GWLP_USERDATA before posting WM_CLOSE, which caused WM_DESTROY's null-
pointer guard to skip the render_target = None cleanup. This left the
ID2D1HwndRenderTarget alive past HWND destruction, and D2D freed its
HwndPresenter during WM_NCDESTROY while the COM wrapper still held a
reference - a second path to the same crash for any message queued
between WM_NCDESTROY and WM_QUIT. The premature GWLP_USERDATA clear has
been removed; WM_DESTROY already handles it correctly after releasing
the render target.
2026-03-29 19:09:13 -07:00
pro470 d7580d2271 feat(wm): add apply state socket message
This allows clients to send a state and get it applied to the wm.

By using the already there apply_state function we have all the
safety we need to apply the sent state.

This is also intentionally not open for the komorebic binary because I
think the average user doesnt need such a command and this is much safer
also to not give them the ability.
2026-03-22 20:06:43 -07:00
LGUG2Z 8a1447f543 docs(schema): update jsonschema 2026-03-22 16:11:43 -07:00
LGUG2Z 53c81c4596 fix(wm): factor in monocle mode for work area offset rules 2026-03-22 16:11:43 -07:00
omark96 d3779e5a74 feat(wm): add dynamic work area offsets
This commit adds a config setting to let you set custom
work_area_offset_rules for a workspace, following the same schema as
layout_rules.
2026-03-22 16:11:43 -07:00
1337cookie c84fa50fc9 fear(bar): add storage_display_name option 2026-03-22 16:11:43 -07:00
bunnyDrug 41fc316a59 fix(docs): add missing word
Reading the documentation I came across what I think is a missing word
in this sentence.
2026-03-22 16:11:43 -07:00
LGUG2Z 011bcb8bd4 refactor(clippy): apply lints 2026-03-22 16:11:43 -07:00
LGUG2Z dce3c91c22 chore(deps): various dependency bumps 2026-03-22 16:11:43 -07:00
LGUG2Z a9a1e68169 chore(git): add procdump.exe to gitignore 2026-03-21 13:39:50 -07:00
dependabot[bot] cb9a7542a6 chore(deps): bump actions/download-artifact from 7 to 8
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 13:10:32 -07:00
dependabot[bot] 145a0ae003 chore(deps): bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 13:10:22 -07:00