Files
komorebi/docs/linux/dev/COMPARE.md
T
LGUG2Z d781028a2a refactor(repo): move the KDE and macOS docs under docs/ (pure move)
komorebi-for-kde/docs/ -> docs/linux/
komorebi-for-mac/docs/ -> docs/macos/

The Windows docs stay where they are (docs/). Renames only, so links and include paths
that named the old directories are broken at this revision; they are fixed in the revision
that fixes the paths of the whole layout move.
2026-11-05 15:03:27 -08:00

60 KiB

komorebi for KDE Plasma compared with komorebi for Windows and for macOS

STALE (snapshot of 2026-10-27, kfw 173aa802, kfm 125a9a5): this comparison predates the consolidation. The three programs now share crates/ (protocol, config, collections, transport, backend, core, settings, komorebic-core) and one komorebi, komorebic, komorebi-client and komorebi-bar package, so counts of duplicated commands, messages and keys below are out of date, and scripts/compare-gen.py no longer runs against the current tree (it fails with cannot find '^enum SubCommand', because the command definitions moved to crates/komorebic-core). For what is shared and what differs on purpose now, read the repository-level docs/dev/ARCHITECTURE-UNIFIED.md and docs/dev/core-model.md. The platform-difference tables (KWin versus Win32 versus AppKit mechanisms) remain a fair description of the platform layers.

This document compares komorebi for KDE Plasma on KWin and Wayland (kfde) with komorebi for Windows (kfw), the original, and komorebi for macOS (kfm). It is written for a reader who knows komorebi on one of those two systems and wants to know what is the same on KDE, what is different, what KDE does by itself that komorebi does on the other systems, and what is not supported.

It states facts that can be checked against the code, and does not assign value judgments. "kfw draws the borders itself and kfde asks the compositor to" is a fact. "The compositor's borders are better" is not, and is not said here. The reasons behind kfde's choices are in DESIGN.md, where they are opinions and say so.

It is written for kfde, so it leans toward kfde, and it was written from the sources of the three programs, not from running them side by side. Where something could not be verified, it says so. A correction is welcome.

How to read this document

Every table in a marked block is generated by scripts/compare-gen.py from the source trees of the three programs, so a count or a name in it is what the code had when the block was last generated. The text around the tables is written by hand and states what the tables say.

python3 -I scripts/compare-gen.py --update docs/dev/COMPARE.md   # rewrite the blocks
python3 -I scripts/compare-gen.py --check docs/dev/COMPARE.md    # exit 1 when one is stale

The script reads this checkout and checkouts of kfw and kfm (~/komorebi and ~/komorebi-for-mac by default; --kfw and --kfm name others). Continuous integration has neither checkout, so it does not run the script: a change to a command, a message, a key of komorebi.json, an event or a method of Backend is followed by --check on a machine that has them, and by --update and a read of the text around the block when it reports a stale one. The script also checks the hand-written notes it holds (why a Windows command has no counterpart, which KWin call stands in for a Win32 one) against the sources, and the sentences of this document that depend on a generated fact, and stops on a note or a sentence that is no longer true. The revisions it read are:

Name What Read from Revision
kfw komorebi for Windows, the upstream ~/komorebi 173aa802, 2026-10-27
kfm komorebi for macOS ~/komorebi-for-mac 125a9a5, 2026-10-27
kfde this project this repository the commit that has this document

kfm changes often, so the numbers for kfm in particular are those of that revision.

Terms used below:

  • KWin's script (the bridge) is a small JavaScript program that komorebi loads into KWin. It is the only code of kfde that runs inside the compositor, and it contains no window manager logic.
  • A workspace is komorebi's name for one of the desktops of a monitor, in all three programs.
  • A parking desktop is a KWin virtual desktop that nobody looks at, which kfde moves windows to in order to hide them.
  • The strip is the scrolling layout of kfm, in the crate komorebi-scrolling. Since upstream's next branch it is also what the Scrolling layout of komorebi-layouts is laid out with, in kfde too (scrolling-next.md).

What is the same

One tiling model, and the same layouts

The model is the same in all three programs: a list of monitors, each with workspaces, each with containers, each with windows. kfde adds no layer to it. The layouts come from the crate komorebi-layouts, which kfw owns and kfm and kfde use as a git dependency:

DefaultLayout kfw kfm kfde
BSP yes yes yes
Columns yes yes yes
Rows yes yes yes
VerticalStack yes yes yes
HorizontalStack yes yes yes
UltrawideVerticalStack yes yes yes
Grid yes yes yes
RightMainVerticalStack yes yes yes
Scrolling yes yes yes

The crate each tree builds: kfw: komorebi-layouts in the tree; kfm: komorebi-layouts at git rev f08aa55; kfde: komorebi-layouts at git rev f08aa55.

Compared file by file with the crate of kfw, kfm: 0 of 13 source files differ from the ones of kfw; kfde: 0 of 13 source files differ from the ones of kfw.

The layouts of komorebi-layouts are calculated by the same code in the three programs, so the same windows in the same order get the same rectangles, apart from what the window manager takes off for the work area, the padding and the borders. The exception is the Scrolling layout, which is laid out with the strip of komorebi-scrolling in kfm and in kfde, and not in kfw before its next branch (see the section on configuration). What differs between the programs is what the system does with the rectangles: the next sections.

One configuration file, largely portable

A komorebi.json written for kfw or kfm loads in kfde. The keys that kfde does not read are accepted and ignored, not rejected. The tables of the sections on configuration say which keys those are, and why.

Commands

komorebic has the same shape in the three programs: a command writes a message on the socket of the daemon, and the message is a variant of the SocketMessage of komorebi-client. A command's name is the variant of SubCommand in kebab case, which is what the pages of docs/cli are named after.

kfw kfm kfde
Commands komorebic has 192 158 158
In all three 148 148 148
Also in kfw, not in kfm 7 - 7
Also in kfm, not in kfw - 1 1
Only in kfde - - 2
In kfw or kfm, not in kfde 37 9 -
Same command under another name - - 0

No command of kfw or kfm is kept in kfde under another name, not even as an alias.

kfde has the commands kfw and kfm share

These commands exist in all three programs, with the same name. The ones that the tiling and the workspaces are driven with do the same on KDE; where they do it differently (a workspace is a KWin desktop) the section on workspaces says how.

adjust-container-padding, adjust-workspace-padding, animation, animation-duration, animation-fps, animation-style, application-specific-configuration-schema, border, border-colour, border-offset, border-width, cancel-preselect, change-layout, clear-all-workspace-rules, clear-named-workspace-layout-rules, clear-named-workspace-rules, clear-session-float-rules, clear-workspace-layout-rules, clear-workspace-rules, close, close-workspace, configuration, container-padding, cross-monitor-move-behaviour, cycle-empty-workspace, cycle-focus, cycle-layout, cycle-monitor, cycle-move, cycle-move-to-monitor, cycle-move-to-workspace, cycle-move-workspace-to-monitor, cycle-send-to-monitor, cycle-send-to-workspace, cycle-stack, cycle-stack-index, cycle-workspace, data-directory, disable-autostart, display-index-preference, docgen, eager-focus, enable-autostart, enforce-workspace-rules, ensure-named-workspaces, ensure-workspaces, flip-layout, focus, focus-last-workspace, focus-monitor, focus-monitor-at-cursor, focus-monitor-workspace, focus-named-workspace, focus-stack-window, focus-workspace, focus-workspaces, focused-workspace-container-padding, focused-workspace-padding, generate-static-config, global-state, global-work-area-offset, ignore-rule, initial-named-workspace-rule, initial-workspace-rule, layout-ratios, load-resize, log, manage, manage-rule, minimize, monitor-information, monitor-work-area-offset, monocle-focus-behaviour, mouse-follows-focus, move, move-to-last-workspace, move-to-monitor, move-to-monitor-workspace, move-to-named-workspace, move-to-workspace, move-workspace-to-monitor, named-workspace-container-padding, named-workspace-layout, named-workspace-layout-rule, named-workspace-padding, named-workspace-rule, named-workspace-tiling, new-workspace, notification-schema, preselect-direction, promote, promote-focus, promote-swap, promote-window, query, quick-load-resize, quick-save-resize, quickstart, replace-configuration, resize-axis, resize-delta, resize-edge, retile, save-resize, scrolling-layout-columns, send-to-last-workspace, send-to-monitor, send-to-monitor-workspace, send-to-named-workspace, send-to-workspace, session-float-rule, session-float-rules, socket-schema, stack, stack-all, start, state, static-config-schema, stop, subscribe-socket, swap-workspaces-with-monitor, toggle-cross-monitor-move-behaviour, toggle-float, toggle-float-override, toggle-lock, toggle-monocle, toggle-monocle-focus-behaviour, toggle-mouse-follows-focus, toggle-pause, toggle-tiling, toggle-window-based-work-area-offset, toggle-window-container-behaviour, toggle-workspace-float-override, toggle-workspace-layer, toggle-workspace-window-container-behaviour, unmanage, unmanaged-window-operation-behaviour, unstack, unstack-all, unsubscribe-socket, visible-windows, workspace-layout, workspace-layout-rule, workspace-name, workspace-padding, workspace-rule, workspace-tiling, workspace-work-area-offset

kfde has the commands kfw has and kfm lacks, for the features it took from kfw

kfm has no counterpart for these. kfde has them because it has the feature:

Command What the help of kfw says
check Check komorebi configuration and related files for common errors
focus-follows-mouse Enable or disable focus follows mouse for the operating system
toggle-focus-follows-mouse Toggle focus follows mouse for the operating system
toggle-maximize Toggle native maximization for the focused window
toggle-transparency Toggle transparency for unfocused windows
transparency Enable or disable transparency for unfocused windows
transparency-alpha Set the alpha value for unfocused window transparency

focus-follows-mouse and toggle-focus-follows-mouse are hidden from --help in kfw and kfde alike.

kfde has the command kfm has and kfw lacks

Command What it does
completions Generate komorebic CLI completions for the target shell

kfde has commands that neither has

These commands are about KDE: a key binding file is turned into KDE's global shortcuts, and the keys the daemon registers itself are looked at and let go.

Command What it does
export-kde-shortcuts Turn an skhdrc into KDE global shortcuts (they work over RDP, unlike evdev hotkey daemons)
hotkeys Key bindings: KDE global shortcuts from a key binding file, and the keys of the daemon

The user guide on key bindings describes them. hotkeys has subcommands: komorebic hotkeys compile does what export-kde-shortcuts does for all three formats, and state, reload and release act on the keys the daemon has registered.

kfde does not start helper programs

komorebic start of kfw can start whkd, AutoHotkey, the bar and masir along with the daemon. In kfde the daemon reads the key binding files itself and there is no bar, so start has one flag the others share and none of the flags that launch another program:

Command Flag kfw kfm kfde
start --ahk yes - -
start --await-configuration yes - -
start --bar yes yes -
start --clean-state yes - -
start --config yes yes yes
start --ffm yes - -
start --masir yes - -
start --tcp-port yes - -
start --whkd yes - -
stop --ahk yes - -
stop --bar yes yes -
stop --ignore-restore yes yes yes
stop --masir yes - -
stop --whkd yes - -
check --configuration-only - no command yes
check --komorebi-config yes no command yes

kfde has no counterpart for these commands

Every command that kfw or kfm has and kfde lacks is here, with the reason. A group that is a "Win32 quirk" is a workaround for how Windows treats windows, which a Wayland compositor does not need.

Group Command In Why kfde has no counterpart
AutoHotkey and PowerShell ahk-app-specific-configuration kfw Writes application rules for komorebi.ahk.
AutoHotkey and PowerShell complete-configuration kfw Part of the legacy komorebi.ahk and komorebi.ps1 configurations.
AutoHotkey and PowerShell pwsh-app-specific-configuration kfw Writes application rules for a PowerShell script.
AutoHotkey and PowerShell reload-configuration kfw Part of the legacy komorebi.ahk and komorebi.ps1 configurations.
AutoHotkey and PowerShell watch-configuration kfw Part of the legacy komorebi.ahk and komorebi.ps1 configurations. komorebi.json is watched always.
Win32 quirks alt-focus-hack kfw Works around the rules Windows has for taking the foreground.
Win32 quirks force-focus kfw Works around the rules Windows has for taking the foreground.
Win32 quirks identify-layered-application kfw A Win32 window style. There is no counterpart in a Wayland compositor.
Win32 quirks identify-object-name-change-application kfw A Win32 event some applications send on launch.
Win32 quirks identify-tray-application kfw A Win32 notion: an application that closes to the system tray.
Windows tooling bar-configuration kfw komorebi-bar is not ported: the Plasma panel is used, and the work area accounts for it.
Windows tooling gui kfw Launches komorebi-gui, which is not ported.
Windows tooling kill kfw Stops the helper programs komorebic start launches on Windows (whkd, AutoHotkey, the bar, masir). It launches none on KDE.
Windows tooling toggle-shortcuts kfw Toggles komorebi-shortcuts, which is not ported.
Windows tooling whkdrc kfw Shows the path of the whkd file. The daemon reads key binding files itself.
borders border-implementation kfw How a Win32 border window is drawn. The compositor draws the border.
borders border-style kfw How a Win32 border window is drawn. The compositor draws the border.
borders identify-border-overflow-application kfw A border window that overflows is a Win32 problem. The compositor draws the border.
borders invisible-borders kfw The size of the invisible frame Windows adds around a window.
custom layouts load-custom-layout kfw Not supported yet.
custom layouts named-workspace-custom-layout-rule kfw Not supported yet.
custom layouts named-workspace-custom-layout kfw Not supported yet.
custom layouts workspace-custom-layout-rule kfw Not supported yet.
custom layouts workspace-custom-layout kfw Not supported yet.
hiding restore-windows kfw Debugging command for windows komorebi hid itself. On KDE stop and the next start send parked windows home.
hiding window-hiding-behaviour kfw One native mechanism hides windows, so there is nothing to choose.
kfm scrolling strip center-focus kfm Belongs to the scrolling strip of kfm (komorebi-scrolling). The Scrolling layout of kfde is the one of komorebi-layouts.
kfm scrolling strip consume kfm Belongs to the scrolling strip of kfm (komorebi-scrolling).
kfm scrolling strip expel kfm Belongs to the scrolling strip of kfm (komorebi-scrolling).
kfm scrolling strip scrolling-layout-full-width kfm Belongs to the scrolling strip of kfm (komorebi-scrolling).
kfm scrolling strip toggle-column-stack kfm Belongs to the scrolling strip of kfm (komorebi-scrolling).
license license kfw and kfm No license check was ported.
license splash kfw and kfm No splash screen was ported.
macOS permissions kfm Shows the System Settings dialogs macOS asks for. KDE asks for none.
monitors monitor-index-preference kfw Identifies a monitor by its size. kfde has display-index-preference, which names the display, as kfm does.
named pipes subscribe-pipe kfw A named pipe is a Windows facility. Subscribers use a Unix domain socket.
named pipes unsubscribe-pipe kfw A named pipe is a Windows facility. Subscribers use a Unix domain socket.
stackbar stackbar-mode kfw komorebi draws the tab bar of a stack on Windows. KDE has no counterpart; a stack is cycled with cycle-stack and focus-stack-window.
title bars remove-title-bar kfw A window without a title bar has no shadow in KWin, and so no border.
title bars toggle-title-bars kfw A window without a title bar has no shadow in KWin, and so no border.
upstream applications file convert-app-specific-configuration kfw Maintains the YAML file of the Windows applications list.
upstream applications file fetch-app-specific-configuration kfw and kfm Not supported yet: there is no applications file for KDE upstream, so there is nothing to fetch.
upstream applications file format-app-specific-configuration kfw Maintains the YAML file of the Windows applications list.

The groups "hiding", "borders" and "title bars" are Windows workarounds that KDE does natively, which is the reason the project recorded when it decided not to port them (DESIGN.md). The other reasons say what the code of kfw and kfm shows each command is for. For "upstream applications file": at time of writing (2026-10-28) no applications file for KDE exists upstream, so fetch-app-specific-configuration has nothing to fetch. The configuration key that points at a file of application rules works (see the section on keys), and the user supplies the file.

The socket protocol

The messages that komorebi-client sends are the variants of SocketMessage. The counts follow the ones of the commands, since nearly every command is one message.

kfw kfm kfde
SocketMessage variants 187 153 156
In all three 148 148 148
In kfde and kfw, not kfm 6 - 6
In kfde and kfm, not kfw - 0 0
Only in kfde - - 2
In kfw or kfm, not in kfde 33 5 -

Of the messages kfde has, these are not in both of the other programs:

  • Only in kfde: HotkeysState, ReloadHotkeys.
  • In kfde and kfw, not in kfm: FocusFollowsMouse, ToggleFocusFollowsMouse, ToggleMaximize, ToggleTransparency, Transparency, TransparencyAlpha.
  • In kfde and kfm, not in kfw: none.

The two messages that only kfde has are the ones behind komorebic hotkeys state and komorebic hotkeys reload. The names of the variants are the same in the programs that share them. Whether every variant has the same payload in the three programs was not compared. Subscribers use a Unix domain socket, as in kfm and, next to named pipes, in kfw.

Configuration

kfde reads the keys of komorebi.json that both others read

The counts, by struct of static_config.rs. "Not read by kfde" is the number of keys that kfw or kfm has and kfde does not:

Struct kfw kfm kfde All three kfw and kfde kfm and kfde kfde only Not read by kfde
StaticConfig (top level) 53 34 37 30 4 3 0 20
WorkspaceConfig (workspace) 22 19 20 19 1 0 0 2
MonitorConfig (monitor) 8 9 9 8 0 1 0 0
Total 83 62 66 57 5 4 0 22

The keys that kfw, kfm and kfde all read, which mean the same, apart from what the next sections say:

  • top level (StaticConfig): animation, app_specific_configuration_path, border, border_colours, border_offset, border_width, cross_boundary_behaviour, cross_monitor_move_behaviour, default_container_padding, default_workspace_padding, display_index_preferences, float_override, float_override_placement, float_rule_placement, floating_applications, floating_layer_behaviour, floating_layer_placement, floating_window_aspect_ratio, global_work_area_offset, ignore_rules, layout_defaults, manage_rules, monitors, monocle_focus_behaviour, mouse_follows_focus, resize_delta, theme, toggle_float_placement, unmanaged_window_operation_behaviour, window_container_behaviour.
  • workspace (WorkspaceConfig): apply_window_based_work_area_offset, container_padding, float_override, floating_layer_behaviour, initial_workspace_rules, layout, layout_flip, layout_options, layout_options_rules, layout_rules, name, tile, wallpaper, window_container_behaviour, window_container_behaviour_rules, work_area_offset, work_area_offset_rules, workspace_padding, workspace_rules.
  • monitor (MonitorConfig): container_padding, floating_layer_behaviour, wallpaper, window_based_work_area_offset, window_based_work_area_offset_limit, work_area_offset, workspace_padding, workspaces.

kfde reads keys from kfw and keys from kfm

kfde reads the keys kfw has for the features it took from kfw, and the ones kfm has that kfw does not:

Key Level Also in What it is
focus_follows_mouse top level kfw Focus the window under the pointer.
transparency top level kfw Add transparency to unfocused windows (default: false)
transparency_alpha top level kfw Alpha value for unfocused window transparency 0-255 (default: 200)
transparency_ignore_rules top level kfw Individual window transparency ignore rules
border_radius top level kfm Radius of window borders
tabbed_applications top level kfm Accepted so that a configuration written for another platform loads, and ignored: a KWin window has no native tabs
titleless_applications top level kfm Accepted so that a configuration written for another platform loads, and ignored: a KWin window is always a window
initial_window_placement_rules workspace kfw Initial window placement rules that determine where new tiled windows are placed in the container list.
window_hiding_position monitor kfm Accepted so that a configuration written for another platform loads, and ignored: there is no hiding position on KDE Plasma

No key of komorebi.json is read only on KDE

none

That is by decision: a file should load on every platform that has the feature (why). The keys that mean something else on KDE, or do less, are these:

Key Level What is different on KDE
border_width top level At most 20 px: the effect draws the ring in the window's shadow, and a window without a shadow has none. While borders are drawn, the layout leaves border_width plus border_offset on each side of a window, also past 20.
border_colours top level Six colors, one for each kind of window, when komorebi's build of the border effect is installed. With the stock effect only single and unfocused take effect, and komorebi says so once in its log.
animation top level style is the Qt easing curve of that name (EaseInOutSine is InOutSine) and a custom cubic Bezier curve is an ease out, when komorebi's build of the Geometry Change effect is installed; the stock effect always eases out exponentially. fps is read and ignored: the compositor animates at the refresh rate of the screen.
focus_follows_mouse top level Both implementations kfw names (Windows and Komorebi) ask KWin for the same thing: its focus policy, put back as the user had it when it is turned off or komorebi exits.
transparency top level KWin's window opacity, set for the unfocused windows of each monitor's shown workspace by kfw's rules (the focused window and a monocled one stay opaque).
theme top level A theme by name or a palette of your own gives the six border colors, and the palette of a wallpaper takes over from it when its workspace is shown.
wallpaper workspace Set through the Plasma shell, which keeps one wallpaper for each screen and not for each desktop, so komorebi changes it as the workspace in front changes. The picture fills the screen.
workspaces monitor Workspace i is KWin's desktop i on every monitor, so every monitor has as many desktops as the largest workspace count, and where the names differ the desktop takes the first monitor's.
app_specific_configuration_path top level Read as in kfm and kfw. There is no applications file for KDE upstream, so komorebi has none to download.

Some keys are read and do nothing on KDE

They are read so that a file written for another platform loads, and komorebic check lists them once as accepted and ignored:

Key Level Also in Effect on KDE
tabbed_applications top level kfm Read, so that a file written for kfm loads, and does nothing on KDE.
titleless_applications top level kfm Read, so that a file written for kfm loads, and does nothing on KDE.
window_hiding_position monitor kfm Read, so that a file written for kfm loads, and does nothing on KDE.
animation.fps nested kfw, kfm Read, and ignored: the compositor animates at the refresh rate of the screen.

Some keys are not read at all

These are keys of kfw or kfm that kfde does not have. They are not an error: serde skips a key it does not know, so the file loads, and komorebic check tells them apart from a typo.

Key Level In Why kfde does not read it
bar_configurations top level kfw komorebi-bar is not ported.
border_implementation top level kfw How a Win32 border window is drawn. The compositor draws the border.
border_overflow_applications top level kfw How a Win32 border window is drawn. The compositor draws the border.
border_style top level kfw How a Win32 border window is drawn. The compositor draws the border.
border_z_order top level kfw How a Win32 border window is drawn. The compositor draws the border.
invisible_borders top level kfw The size of the invisible frame Windows adds around a window.
layered_applications top level kfw A Win32 window style. There is no counterpart in a Wayland compositor.
minimum_window_height top level kfw Marked DISCOURAGED in komorebi for Windows. Commented out in kfde.
minimum_window_width top level kfw Marked DISCOURAGED in komorebi for Windows. Commented out in kfde.
monitor_index_preferences top level kfw Identifies a monitor by its size. kfde reads display_index_preferences, which names the display, as kfm does.
object_name_change_applications top level kfw A Win32 event some applications send on launch.
object_name_change_title_ignore_list top level kfw A Win32 event some applications send on launch.
remove_titlebar_applications top level kfw A window without a title bar has no shadow in KWin, and so no border.
scrolling top level kfm The ScrollingConfig of the strip in kfm (komorebi-scrolling). kfde has the Scrolling layout of komorebi-layouts, tuned with layout_options.
slow_application_compensation_time top level kfw A Win32 workaround for applications that are slow to move.
slow_application_identifiers top level kfw A Win32 workaround for applications that are slow to move.
stackbar top level kfw KDE has no counterpart of the tab bar komorebi draws on Windows.
tray_and_multi_window_applications top level kfw A Win32 notion: an application that closes to the system tray.
window_handling_behaviour top level kfw A Win32 quirk of how windows are moved.
window_hiding_behaviour top level kfw One native mechanism hides windows, so there is nothing to choose.
custom_layout workspace kfw Custom layouts are not supported yet.
custom_layout_rules workspace kfw Custom layouts are not supported yet.

The nested structs have the same keys, except for these:

Struct kfw kfm kfde In kfw, not kfde In kfm, not kfde
BorderColours 6 6 6 - -
ThemeOptions 11 8 8 stackbar_background, stackbar_focused_text, stackbar_unfocused_text -
Wallpaper 3 3 3 - -
AnimationsConfig 5 4 4 ghost_movement -

There is one more difference that is not a key. kfm has a scrolling key. kfde has the Scrolling layout of komorebi-layouts, which is laid out with the same strip as kfm's, with columns that hold several windows and the commands consume, expel, toggle-column-stack, scrolling-layout-full-width, center-focus, expand and pan, and is tuned with layout_options and the command scrolling-layout-columns, which kfw and kfm have too. A scrolling key in a file written for kfm is skipped by kfde. The generated tables above were made before kfde took these commands: scripts/compare-gen.py --update makes them again.

Events, and what drives them

The window manager reacts to events about windows. In kfw the operating system raises them, through SetWinEventHook, and in kfm through the accessibility API and the notification center. In kfde the script in KWin connects to the signals of KWin's workspace and of each window and sends every one to the daemon as a JSON string over D-Bus. The daemon turns a signal into a WindowManagerEvent, the way kfw and kfm turn what they get.

Tree What the OS layer reports Kinds
kfw WinEvent, the events of SetWinEventHook 84
kfm AccessibilityNotification and AppKitWorkspaceNotification 41 and 21
kfde KWinNotification, the signals of the KWin script 16

The events follow kfw's model: each is about a window, and a window is a small Copy handle. kfm's events carry a process id and a window id instead. The table puts the events of kfde next to the ones of the other two that play the same part:

kfde event Source on KDE kfw kfm
Show workspace.windowAdded Show, Uncloak Show
Destroy workspace.windowRemoved Destroy Destroy
FocusChange workspace.windowActivated FocusChange, Raise FocusChange
Minimize window.minimizedChanged, to true Minimize Minimize
Restore window.minimizedChanged, to false - Restore
TitleUpdate window.captionChanged TitleUpdate -
Manage komorebi itself (manage, a rule) Manage Manage
Unmanage komorebi itself (unmanage, a rule) Unmanage Unmanage
MoveStart window.interactiveMoveResizeStarted, for a move MoveResizeStart MoveStart
MoveEnd window.interactiveMoveResizeFinished, after a move MoveResizeEnd MoveEnd
ResizeStart window.interactiveMoveResizeStarted, for a resize MoveResizeStart ResizeStart
ResizeEnd window.interactiveMoveResizeFinished, after a resize MoveResizeEnd ResizeEnd
ResizeStep window.interactiveMoveResizeStepped - -
WorkspaceChange workspace.currentDesktopChanged - SpaceChange
WorkspaceMove window.desktopsChanged - -
OutputChange window.outputChanged - -
MaximizeChange window.maximizedChanged - -
FullscreenChange window.fullScreenChanged - -
Event with no counterpart in kfde In Why
Hide kfw Win32 hides and cloaks windows; kfde has no counterpart because KWin shows and hides windows by desktop, and says so with WorkspaceChange.
Cloak kfw Win32 hides and cloaks windows; kfde has no counterpart because KWin shows and hides windows by desktop, and says so with WorkspaceChange.
MouseCapture kfw Windows tells komorebi that a mouse button went down on a window. KWin reports the start of a move or resize instead.
ScreenLock kfm KDE locks and unlocks the screen with org.freedesktop.ScreenSaver; komorebi hears it in kwin/power.rs and reconciles, with no event of this kind.
ScreenUnlock kfm KDE locks and unlocks the screen with org.freedesktop.ScreenSaver; komorebi hears it in kwin/power.rs and reconciles, with no event of this kind.

Three things about this are worth knowing:

  • A signal reaches the daemon in 0 to 1 ms, from Date.now() in the script to the receipt in the daemon (the spikes, 2026-10-26, KWin 6.8.1 in a virtual machine on HVF).
  • KWin emits windowActivated before windowAdded for a new window, so the script announces the window from the activation, and no event mentions a window before the event that adds it.
  • The script contains no logic. Why and why the commands come back to the script as the answer to a held call are in DESIGN.md.

What the operating system layer is

kfw calls Win32 from WindowsApi, kfm calls the accessibility API and Core Graphics from MacosApi and its modules. kfde puts the whole of what the window manager needs behind one trait, Backend, which a test replaces with FakeBackend (a desktop in memory). The sizes are not a measure of the effort: the Win32 and macOS calls are scattered over more modules than the ones the counts name.

Tree The operating system layer Size
kfw WindowsApi in windows_api.rs 92 public functions
kfm MacosApi in macos_api.rs, and the accessibility, core_graphics and skylight modules 14 public functions in MacosApi
kfde Backend, with FakeBackend for tests and KWinBackend for KWin 31 methods (27 are required, 4 are provided), and a script of 684 lines that runs inside KWin

What each method of Backend is on KDE, and the function of kfw and kfm that does the same job, where there is one:

Backend method On KDE kfw function kfm function
windows workspace.windowList() once, then the events of the bridge enum_windows window_elements
window the table the bridge keeps, fed by events window_rect, exe_path window_by_title
foreground_window workspace.activeWindow foreground_window foreground_window
monitors workspace.screens and clientArea(KWin.PlacementArea, ...), which leaves out the panel load_monitor_information load_monitor_information
cursor_pos workspace.cursorPos cursor_pos cursor_pos
drag_origin the frame the bridge saw at interactiveMoveResizeStarted none none
drag_end the frame the bridge saw at the last interactiveMoveResizeStepped none none
set_rect frameGeometry, and a second request for the position once the size settles position_window set_position
focus workspace.activeWindow = w raise_and_focus_window focus
raise workspace.raiseWindow(w) raise_window raise
hide moves the window to the parking desktop (desktops) hide_window hide
restore moves the window back from the parking desktop (desktops) restore_window restore
minimize minimized = true minimize_window minimize
unminimize minimized = false restore_window unminimize
close closeWindow() close_window close
center_cursor_in the "Move Mouse to Focus" action (MoveMouseToFocus) center_cursor_in_rect center_cursor_in_rect
ensure_workspaces createDesktop on org.kde.KWin.VirtualDesktopManager none none
switch_workspace setCurrentDesktopForScreen none none
set_window_workspace desktops of the window none none
window_workspace desktops of the window none none
current_workspaces currentDesktopForScreen none none
apply_look writes the effects' groups of kwinrc, loadEffect and reconfigureEffect none none
set_border_colours org.komorebi.BorderColours1, which only komorebi's build of the border effect has none none
set_opacity opacity set_transparent none
set_focus_follows_mouse the FocusPolicy of kwinrc, then reconfigure enable_focus_follows_mouse none
set_wallpaper org.kde.PlasmaShell setWallpaper set_wallpaper set_wallpaper
maximize setMaximize maximize_window none

A method with "none" in both columns is something kfw and kfm do not need an operating system call for, because the window manager keeps its own workspaces there, or something they do not have. Why everything is behind one trait is in DESIGN.md.

What KDE does by itself

The table lists what kfw and kfm do themselves and what KDE does, for the features where the compositor, the shell or the desktop has a mechanism of its own. The sections below say what is different for each.

What kfw kfm On KDE
Workspaces komorebi's own workspaces; the windows of the other workspaces are hidden komorebi's own workspaces; the windows of the other workspaces are hidden KWin's virtual desktops, one set per output (Plasma 6.8): workspace i is desktop i on every monitor
Hiding windows (a stack, a monocle) window_hiding_behaviour: cloak, hide or minimize moves the window to a corner of the screen a virtual desktop nobody looks at (komorebi-park)
Borders a topmost tool window per border, drawn by komorebi with Direct2D an NSWindow per border, drawn by komorebi the KDE Rounded Corners effect, with komorebi's patch for one color per kind of window
Animation komorebi moves the window step by step komorebi moves the window step by step the Geometry Change effect, with komorebi's patch for the easing curve
Transparency of unfocused windows SetLayeredWindowAttributes, called by komorebi no key or command KWin's window opacity, through the same patched effect as the borders
Focus follows mouse the Windows setting, or komorebi's own implementation (focus_follows_mouse) no key or command KWin's focus policy; the user's policy is put back when it is turned off or komorebi exits
Maximize toggle-maximize: the Win32 maximize no key or command toggle-maximize: KWin's maximize, which respects the panel. A window the user maximizes is taken out of the layout until restored
Fullscreen windows no handling of its own in the sources no handling of its own, except that the reaper keeps a window that fails to move left alone, and put back in its tile when it leaves fullscreen
Wallpapers the desktop wallpaper of each monitor the desktop picture of each display the Plasma shell, asked over D-Bus, one wallpaper per screen
Hotkeys a separate program: whkd (or AutoHotkey) a separate program: skhd KDE global shortcuts: .desktop files for plain bindings, and the daemon through KGlobalAccel for modes, chords and scopes
Monitors added, removed or changed a reconciliator thread, fed by WM_DISPLAYCHANGE in a hidden window a reconciliator thread, fed by the display reconfiguration callback the same thread, fed by the script (when KWin's list of outputs changes) and by kwin/power.rs; reconcile.rs follows the windows the compositor moved
Sleep, wake and lock power setting and session notifications (register_power_setting_notification, wts_register_session_notification) ScreenLock and ScreenUnlock events PrepareForSleep of logind and ActiveChanged of the KDE screen saver, on D-Bus
Reloading the configuration watch-configuration is about komorebi.ahk and komorebi.ps1; komorebi.json is watched komorebi.json is watched komorebi.json is watched

Workspaces are KWin's virtual desktops

kfw and kfm have a workspace layer of their own: komorebi keeps a list of workspaces for each monitor, and when one is left it hides the windows of that workspace, because Windows and macOS have no virtual desktop API to ask. On KDE, KWin 6.8 can give each output its own current desktop, so komorebi's workspace i is KWin's desktop i on every monitor, and each monitor shows one at a time. A switch is one request per monitor, and KWin shows, hides and focuses. komorebi follows a switch made by something else (the pager, a shortcut, an application raising a window) and answers it with no command. Why is in DESIGN.md.

This has consequences that are measured, or read from KWin's sources:

  • A switch made by an application raising a window produces one focus event and one switch (spike 10, 2026-10-27). Answering a switch with commands makes KWin and komorebi fight: a design in which komorebi did the hiding flipped between two workspaces about 280 times a second, because KWin delivered focus events it had generated before it applied the commands, and komorebi read them as new intent. komorebi therefore issues no commands in reply to a switch.
  • The workspaces are visible in the pager and in the overview: they are the user's desktops, and a stop leaves them in place (it removes the parking desktop).
  • Desktops are global: every monitor has as many as the largest workspace count, and the name of a desktop is shared by the monitors (the first monitor's, where they differ).
  • A window that komorebi does not manage (a dialog, an ignored application) belongs to the desktop it opened on, and goes out of view with it. kfw and kfm hide what a workspace holds (its containers, its floating windows and its monocle) when they leave it, so a window they do not manage stays visible across workspaces.
  • The floor is KWin 6.8, because per-output desktops arrived there. Older KWin versions were not tested. At time of writing (2026-10-28) the versions run are 6.8.1 (the spikes) and 6.8.2 (the VM suites), in Plasma 6.8.1 and 6.8.2.
  • komorebi turns PerOutputVirtualDesktops on in kwinrc when it starts and puts it back when it exits. It edits the file itself: kwriteconfig6 was not on the path of the user service in the first live run.

Hiding windows: cloaking, a corner, or a parking desktop

komorebi hides windows for a stack (the other windows of a container) and for a monocle (the other containers). kfw offers three ways, chosen with window_hiding_behaviour: cloak (an undocumented Win32 call), minimize, and the end-of-life SW_HIDE. kfm moves the window to a bottom corner of its display, which window_hiding_position of a monitor chooses. kfde moves the window to a parking desktop and moves it back, and there is nothing to choose. The scrolling layout needs none of this: KWin keeps a window at a position outside the screen when it is moved there.

Three ways were measured on six tiled windows (spike 3, 2026-10-26, KWin 6.8.1):

Way Spurious focus events Geometry restored
minimize 2 (KWin picks a new active window as each window minimizes) 6 of 6
parking desktop 0 6 of 6
a position outside the screen 0, but the windows stay mapped on the current desktop (task switcher, overview) 6 of 6

KWin saves a virtual desktop in kwinrc the moment it is made, so a run that dies leaves the parking desktop for the next session. komorebi removes it on exit, and the next start finds a stale one, sends its windows home and removes it. A parked window still appears in the task manager unless that is set to show the current desktop only (by construction, not measured). Why a parking desktop is in DESIGN.md.

Borders: komorebi draws them, or the compositor does

kfw draws a topmost tool window for each border, with Direct2D, and kfm an NSWindow for each. kfde draws nothing: the KDE Rounded Corners effect draws the outer outline of every window, and komorebi writes its settings to kwinrc and tells it to read them again. KWin keeps the copy of kwinrc it read at start, so komorebi asks for a reconfiguration every time, also straight after it loads the effect.

The limits, as of 2026-10-27:

  • The ring is drawn in the window's shadow, so a window without a shadow has none, and its thickness is at most 20 px. That is why kfde has no counterpart of remove_titlebar_applications: removing the title bar removes the shadow.
  • The stock effect has a color for the active window and one for the others. komorebi's build of it takes a color for each window over D-Bus (org.komorebi.BorderColours1), which gives the six kinds of kfw's rules: single, stack, monocle, floating, unfocused and unfocused_locked. Without that build, every kind falls back to single and unfocused, and komorebi says so once in its log.
  • KDE Rounded Corners 0.10.0 draws the ring about 24 px too low on KWin 6.8 (measured on screenshots of KWin 6.8.1). kfde builds 0.11.0, which nixpkgs has too at time of writing (2026-10-28).
  • KWin finds effects when it starts, so an effect installed later is not loaded until the next login.

Why the compositor draws the borders, why the effects are patched and why the border is the outer outline are in DESIGN.md.

Animation: komorebi moves the window step by step, or the compositor animates

kfw and kfm animate by moving the window a little at a time, at animation.fps. kfde sends the final rectangle and the Geometry Change effect animates the move, so fps has nothing to map to: the compositor animates at the refresh rate of the screen. The styles are Qt's easing curves of the same name, which komorebi's build of the effect reads from a Curve setting. The stock effect eases out exponentially, whatever the style says. Cross-fading is off.

KWin refuses effects that animate under software rendering, which includes every virtual machine the tests run in, unless KWIN_EFFECTS_FORCE_ANIMATIONS=1 is in KWin's environment. A screenshot cannot show a short animation, because spectacle takes seconds and the curve is front-loaded: the look test uses a 30 s animation to see the effect at work.

Transparency, themes and wallpapers

Transparency is KWin's window opacity, set from the same rules as kfw. A window drawn through the border effect comes from a texture with its opacity in it, which the effect redrew only when the contents changed. komorebi's build redraws it when the opacity changes.

A wallpaper is set through the Plasma shell, over D-Bus (org.kde.PlasmaShell), for one screen. The shell keeps one wallpaper for each screen, not for each virtual desktop, so komorebi sets it when a workspace comes into view, as kfw and kfm do. If the shell is not running, komorebi tries again for about half a minute and then says once that it leaves wallpapers alone. A palette made from a wallpaper is cached in $XDG_CACHE_HOME/komorebi, by the name of the picture and a hash of its path, size and time of change; a large picture takes seconds (not measured further).

Focus follows mouse

kfw has two implementations to choose from, Windows and Komorebi, and calls the first legacy. kfm has none. kfde asks KWin to use its focus policy, "focus follows mouse", with the same result for both names, and puts the user's policy back when the setting is turned off or komorebi exits. KWin waits for the pointer to settle before it moves the focus: DelayFocusInterval, 300 ms by default in KWin 6.8.1. kfde sets it to 0 while the setting is on, and puts the user's value back with the policy; why 0 is not recorded (DESIGN.md).

Maximize and fullscreen

toggle-maximize is kfw's command, and does KWin's maximize on KDE: the window leaves the layout and goes back to its tile when toggled again, and the panel is respected. A window that the user maximizes (the title bar, a double click, a shortcut) is the same from then on: the commands that act on the layout do nothing until it is restored. Why is in DESIGN.md. A fullscreen window covers its monitor, komorebi never moves it, and it is put back in its tile when it leaves fullscreen. kfw has no code for fullscreen, and kfm has one mention of it in its reaper.

Dragging

All three programs let the user move a tile by dragging it and resize it by dragging an edge. KWin reports where the pointer has the edge at every step of a resize (interactiveMoveResizeStepped), so in kfde the neighbors follow the edge while it is dragged, and not only when it is let go. The way a drag is measured, from the frame the window had when the drag began, and granted as far as the layout can take it, is written down in drag-resize.md, for the three programs. The frame KWin reports at the end of a resize lags the pointer by a moment (PLATFORM.md). kfm reads the frame of a window with an accessibility call when it needs it (MacosApi::window_rect); whether that frame lags was not measured.

Key bindings

kfw and kfm leave the keys to a separate program: kfw uses whkd, or AutoHotkey, kfm uses skhd, and the program runs komorebic. On Plasma under Wayland no program sees the keys of another, so kfde cannot do that: the keys are KDE's global shortcuts, in one of two ways, and komorebi reads the same three formats a hotkey daemon would (skhdrc, whkdrc, sxhkdrc).

kfw kfm kfde
Who sees the keys whkd, or AutoHotkey skhd KWin, through KDE's global shortcuts
The file the user writes whkdrc skhdrc skhdrc, whkdrc or sxhkdrc, each read by a parser written for komorebi

The measurements that decide when each is used, at time of writing (2026-10-27), on aarch64 under HVF with four cores, KWin 6.8.1:

What Result
A static shortcut, key to the start of its Exec 165 to 436 ms, median 233 ms, 8 runs of 8
The daemon, key to the signal it gets median 2.5 ms (min 2.2, max 3.9), 20 of 20 delivered
60 calls to setInactive and setShortcutKeys, pipelined answered in 2.4 ms in total
A native multi-key shortcut (Meta+A, B) did not fire: the second key reached the focused client
A client killed after registering a key the grab stays, and the key is swallowed, until unRegister

The last two are why the daemon emulates chord chains, and why the module's unit releases the keys when the daemon stops. A static shortcut works with the daemon stopped and shows in System Settings, but costs about 0.2 seconds on each press; the daemon's keys are only as reliable as the daemon. The default mode is the daemon for every binding (keybindings.mode = "engine"), which these measurements favor (why, which also says that the default is still open for a decision), and auto and static use shortcut files for the bindings that can be one.

What is not emulated, and approximated, is listed in the user guide: side-specific modifiers, mouse buttons, skhd's fn, and the sxhkd chain that a wrong key does not end. The reasons are in DESIGN.md: why some bindings are shortcut files and why the engine grabs keys instead of using KDE's key sequences.

Keystrokes over RDP: KDE's shortcuts sit inside KWin's input pipeline and see them, and swhkd reads the kernel's input devices and would not (read from the sources of KRDP and swhkd, not run). Keys sent over RDP to a session were not verified end to end: the scripted client fails NTLM against KRDP, and a real client was not tried.

Multiple monitors, sleep and lock

All three programs have a monitor reconciliator: a thread that takes notifications one at a time to move the windows of a monitor that went away and to bring them back. In kfw a hidden window (WM_DISPLAYCHANGE) feeds it, and in kfm a display reconfiguration callback. In kfde the script feeds it, with the whole list of outputs whenever KWin's changes, and so does the code that hears of a wake or an unlock. The daemon takes the windows of an unplugged monitor over (each container, a stack whole, to the workspace of the same number), keeps what is left of the monitor for when it comes back, and follows a window that something else moved to another output. A moment after each notification (1.5 s) it looks at every window again, because KWin has moved some. A monitor that changes size or scale is tiled again: every coordinate komorebi uses is KWin's logical one. Why is in DESIGN.md.

For the screen lock, kfm has ScreenLock and ScreenUnlock events, and kfw registers for power and session notifications. kfde listens on D-Bus: logind's PrepareForSleep for a wake, and KDE's screen saver ActiveChanged for a lock. With the screen off or locked KWin sends clients no frame callbacks, so what komorebi asked for in that time is not done: komorebi waits 600 ms after an unlock and 2.5 s after a wake and looks at every window again. The lock screen is a window of KWin's that is in no task list and on every desktop; komorebi does not manage it.

At time of writing (2026-10-27) a real suspend and resume was not tested: the QEMU guests have no way to wake from one. The signals are tested on private buses, and the lock screen against a real KWin in a Plasma session. PLATFORM.md says so too.

Checking the configuration

kfw and kfde have komorebic check, and kfm has none. kfw's reads the files in the configuration home (komorebi.json, komorebi.ahk and whkdrc among them) and shows what is wrong with them. kfde's reads komorebi.json the way the daemon will, tells a key of another platform from a typo (with the nearest key that is a setting), looks at the key binding file and at whether it has been made into KDE shortcuts, and then looks at the desktop: a Plasma session on Wayland, KWin 6 on the session bus, komorebi's script in KWin, which build of each effect is installed, the Plasma shell when there are wallpapers, and what starts komorebi at login. It exits with status 1 when something stops komorebi from working (a file that is not JSON, no KWin), and 0 with warnings when it only will not do what was meant. --komorebi-config FILE checks another file and --configuration-only leaves the desktop out; the first flag is kfw's too.

kfw's check also asks GitHub for the latest release. kfde's does not look for a newer release, and it does not know the AutoHotkey and PowerShell files of Windows.

What kfw and kfm have that kfde does not

From the tables above, and from what has no row in them:

  • The bar. komorebi-bar is not ported: the Plasma panel is used, and the work area accounts for it (a panel moved is a change of the work area, and the tiles are made again).
  • The stackbar and its settings. A stack is cycled with cycle-stack and focus-stack-window.
  • Custom layouts, which kfw has and kfm does not. Not supported yet.
  • The Win32 workarounds of the "Win32 quirks" group, the AutoHotkey and PowerShell configurations, the named-pipe subscribers, and whkd as a separate program.
  • The license check and the splash screen of the macOS and Windows builds.
  • The tab and title bar commands (remove-title-bar, toggle-title-bars).

What kfde has that kfw and kfm do not

  • Native workspaces: they show in KWin's pager and overview, and KWin focuses the window of the desktop it switches to.
  • Borders, animation, transparency and focus follows mouse as a compositor does them, and check for a desktop.
  • Key bindings that work from a physical keyboard, the QEMU console and RDP, with modes, chords and scopes realized by the daemon, and the skhdrc, whkdrc and sxhkdrc formats.
  • A NixOS module (services.komorebi) with a user unit, the effects, and the key bindings as options, and a version check of KWin. kfm has a module for nix-darwin, in its own form.
  • A test suite that runs the window manager against a real KWin in virtual machines, and a Backend that tests replace with a desktop in memory. vm-tests.md describes it.

Costs, measured

At time of writing (2026-10-27), on an Apple silicon Mac, in virtual machines under HVF, KWin 6.8.1 (Plasma 6.8.1, nixpkgs d3b13ad of 2026-10-24). No number here compares kfde with kfw or kfm: nothing of the same kind was measured on them.

What Result Source
A retile of 10 foot windows on a 3840x2160 output, from the answer to Poll to the last frame KWin reported (5.5 windows moved on average) p50 17.3 ms, p95 27.7 ms, max 31.4 ms vm-latency, PLATFORM.md
The same channel alone, 20 windows in each batch p50 16 ms, p95 19 ms, max 20 ms the spikes
A client's answer to a frame exact for Qt, GTK 4 down to its minimum, XWayland; foot rounds to its cells; GTK 4 Calculator and Firefox keep a minimum size vm-client-geometry, PLATFORM.md
A window resized off the screen KWin keeps 100 px of it on an output PLATFORM.md
KWin restarting under a running daemon the script is loaded again about 1 s after KWin is back the spikes

What was not measured is listed in PLATFORM.md: a real suspend and resume, KWin before 6.8, activities, outputs beyond a second one, applications in a sandbox, keys from an RDP client and graphics other than software rendering, among others.