komorebi.json is described as a JSON Schema, made from the Rust types that read it: komorebic prints the schema of komorebi.json, of an applications.json, of a notification and of a message, and the daemon answers the same four questions on its socket. schema.json and schema.asc.json are committed (just jsonschema). The module's config option is typed from the schema: schemars-nixgen makes nix/komorebi-options.nix from schema.json (just nixgen), so a layout komorebi does not have, or a value of the wrong kind, does not evaluate. The module writes config as a komorebi.json and gives it to the daemon with --config. configFile and the file the module writes are checked against the schema when the system is built. applicationSpecificConfiguration takes files from GitHub, and there are none by default, since no community file for Plasma is known. Two checks keep the generated files honest without the tools that make them. schema-fresh compares the schemas with what komorebic prints, and the hash on the first line of the options with the schema. nix-options-validation evaluates the options against the example configuration, written in Nix and in JSON. nixos-module-eval also evaluates the module with the sources turned off: with applicationSpecificConfiguration.enable = false the sources are left out of the config that is written, and with no config either there is nothing to write and the unit passes no --config. ARCHITECTURE has the guarantee row that names the two checks.
komorebi for KDE
A tiling window manager for KDE Plasma on Wayland, in the family of komorebi for Windows and komorebi for Mac. The daemon decides where each window goes, and KWin, the window manager of Plasma, does the moving and the drawing.
Installing it
On NixOS, the flake's module, services.komorebi, installs komorebi and
komorebic and starts komorebi with every Plasma session: one flake input,
one module line and services.komorebi.enable = true. Elsewhere, both are
built from source. docs/installation.md has both
ways. There are no prebuilt binaries: the Komorebi license does not allow third
parties to redistribute them.
Running it locally
nix develop
cargo run --bin komorebi --locked -- --version
cargo run --bin komorebi --locked -- --log-level debug
cargo run --bin komorebi --locked -- --fake-backend
Inside a Plasma Wayland session, komorebi loads a script into KWin, reads the
monitors and the windows that are open, tiles them, and tiles the windows again
whenever one opens, closes, takes the focus, is minimized or comes back, until
it is stopped with Ctrl-C. It needs KWin 6.8 or newer;
docs/dev/PLATFORM.md has what it expects of the
session. Outside a Plasma session it exits with an error that says what is
missing. --fake-backend runs the window manager against an in-memory desktop
with one monitor and no windows instead.
While it runs, the daemon takes messages on a Unix socket, komorebi.sock in
$XDG_RUNTIME_DIR/komorebi (the local data directory when there is no runtime
directory): a message can ask for the state of the window manager, ask which
monitor, workspace or container has the focus, give the focus to another
window, move the focused window to another tile, change the layout, resize a
tile, float a window, give one window the whole work area or have KWin maximize it, retile every
monitor, or stop the daemon. komorebi-client writes them, and komorebic,
the command line, is how a person sends them. komorebic start runs the
komorebi on the PATH, and passes it what follows a --. In a Plasma
session:
cargo build --locked
export PATH="$PWD/target/debug:$PATH"
komorebic start
komorebic state
komorebic stop
komorebic start -- --fake-backend tries the daemon and komorebic without a
compositor, against the in-memory desktop.
nix develop provides the Rust toolchain that rust-toolchain.toml
selects, a nightly rustfmt, and the tools just check and the flake's checks
use. With direnv, the .envrc does it on
cd. On Linux, nix run .#komorebi -- --version builds and runs the daemon
without a checkout of the toolchain.
Crates
komorebi: the daemon, and the library behind it. The library has the vocabulary the window manager is written in (core), theBackendtrait through which it reaches a compositor and the in-memory desktop the tests use (backend), the backend that reaches KWin through a script it runs and edits the one setting of KWin's it needs in the user'skwinrcand the parking desktop it hides the back of a stack and the windows behind a monocle on (kwin), the model of what it tiles (monitor,workspace,container,window), the window manager that keeps the model (window_manager), loads it from the backend (desktop) and changes it when the compositor reports an event (window_manager_event,window_manager_event_listener,process_event), theRingthat holds monitors, workspaces and containers, and the lists in which some elements are locked in place. It answers the messages on the daemon's socket (core::SocketMessage,process_command), shows its state (state), sends its subscribers a notification of each event and message it handles (notify_subscribers), and is configured by akomorebi.jsonthat it reads and reads again when it changes (static_config).komorebi-client: a library that sends messages to the daemon, reads its answers, subscribes to its notifications, and re-exports the types they are made of.komorebic: the command line. A subcommand that asks something of the daemon is one message on its socket;start,configuration,log,data-directory,completionsand the hiddendocgenwork without the daemon.version: the version string every binary reports.
The layouts, which turn a work area and a number of containers into a rectangle
for each, are komorebi-layouts from komorebi,
a git dependency at the revision Cargo.toml pins.
Checks
just check runs the formatting check, clippy with warnings denied, the tests,
rustdoc with warnings denied, and the type check and tests of the bridge (no
package.json: tsc and node come from the dev shell), inside nix develop.
nix flake check runs the same checks as derivations, plus cargo deny, the
TOML and Nix formatting, the tests under cargo nextest, and
nixos-module-eval, which evaluates the NixOS module in real NixOS systems, on
Linux and on an Apple silicon Mac, schema-fresh, which compares the schemas and
the options made from them with the Rust types, and nix-options-validation,
which checks the example configuration, in Nix and in JSON, against the schema. On Linux it also boots every VM test, which
needs KVM. A Mac runs the tests against the fake desktop, since KWin only exists
on Linux. nix fmt formats every file the checks look at.
The VM tests boot NixOS with Plasma and run a real KWin, on machines configured
with the NixOS module, so every one of them runs komorebi as the module installs
it. just vm NAME runs one; docs/dev/vm-tests.md has
the details, including how to run them on a Mac.
How it fits together
The daemon decides where every window goes and asks the compositor to put it
there. Everything it needs from the compositor is behind one trait, Backend,
so the window manager logic runs against a real compositor and against an
in-memory fake desktop alike. docs/dev/ARCHITECTURE.md
describes the trait and where each of its rules is enforced.
On KDE Plasma the backend is in two parts. A script that komorebi loads into KWin, the bridge, reports what KWin sees over the session bus, and carries out the commands it is given as the answer to a call it holds open. The daemon keeps a table of the windows and monitors from those reports, answers the window manager's questions from it, and turns each report into an event for the window manager. If KWin restarts, the daemon loads the bridge into the new one.
komorebi's workspaces are KWin's own virtual desktops: workspace i is desktop
i, and each monitor shows a desktop of its own, which the daemon has KWin do by
turning on its per-output virtual desktops (the setting is put back when the
daemon stops). Showing a workspace is a request to KWin, which shows the
windows of that desktop, hides the others, and gives the focus to one of them.
When something else switches a monitor to another desktop (a pager, a shortcut,
an application raising a window of another desktop), or puts a window on
another desktop, the window manager follows.
The window manager models the desktop as monitors, each with a ring of workspaces, each with a ring of containers, each with a ring of windows. A workspace's layout gives each of its containers a tile of the monitor's work area, and every window of a container is moved onto its tile. At startup the window manager loads the monitors and the windows that are open from the backend and tiles them; after that it changes the model when the compositor reports that a window was shown, closed, focused, minimized or restored, and tiles the workspace in front again. A window open at startup goes to the workspace whose desktop it is on.
A container with more than one window is a stack: the layout gives it one tile,
and the window in front is in sight. The others are hidden, which on KWin means
moved to one more virtual desktop, komorebi-park, that nobody looks at and that
is not a workspace. The daemon makes it when it first needs it, removes it when
it stops, and removes one that a run that did not stop left behind. A monocle
hides the windows of the other containers there too.
Everything else reaches the daemon as a message on its socket: one JSON
SocketMessage per line, handled one at a time with the window manager
locked. A message that asks a question is answered on the same connection. A
message that gives an order changes the model the way an event does: focus
and cycle-focus give the focus to the container on one side of the focused
one in the layout, or to the next one in the workspace's order, and move and
cycle-move swap the focused container with that one and tile the workspace
again. change-layout, cycle-layout and flip-layout tile the workspace in
another layout, layout-ratios and scrolling-layout-columns tune it,
toggle-tiling stops it moving windows and starts it again,
preselect-direction reserves a tile for the next window, resize-edge and
resize-axis move the edges of the focused tile, promote brings the
focused window to the largest tile, toggle-float takes it out of the
layout, toggle-monocle gives its container the whole work area, hiding the
others, and puts it back, toggle-maximize has KWin maximize the focused window
and restore it, toggle-workspace-layer switches the focus commands between the
tiles and the floating windows, and toggle-lock keeps its container at its
place. The scrolling layout is a strip of columns that runs past the edges of
the monitor and scrolls to the window that has the focus. minimize and close
ask KWin to minimize or close the focused window, unmanage takes it out of the
layout until manage takes it back, and toggle-pause stops the daemon acting
on events and orders until it is resumed. The padding commands set the gaps around the tiles, and
the work area offsets keep parts of a monitor free of tiles. Rules decide which
windows are managed at all: ignore-rule keeps an application out of the
layout, manage-rule tiles it whatever the ignore rules say, and
session-float-rule floats the focused window and the windows like it.
focus-workspace shows another workspace, and move-to-workspace and
send-to-workspace put the focused window on another workspace, with and
without showing it; their cycle- and -last-workspace forms go to the next,
the previous or the last workspace, workspace-name names a workspace for
focus-named-workspace and the other named commands, and new-workspace and
close-workspace add and remove one.
At the edge of a monitor, focus and move cross to the monitor on that side;
focus-monitor, move-to-monitor, send-to-monitor and their kind work on the
monitors, and eager-focus finds the window of an application wherever it is.
stack puts the focused window and the one next to it in one stack, unstack
takes it out again, and stack-all and unstack-all do it for a whole
workspace; cycle-stack and focus-stack-window bring another window of a
stack to the front, and cycle-stack-index moves the front one along it.
toggle-window-container-behaviour makes a window that opens join
the focused stack, and toggle-float-override makes it float.
The daemon is configured by komorebi.json: the file --config names, or the
one in ~/.config/komorebi. It reads the file when it starts, once it has the
monitors, and gives each monitor the workspaces the file lists for it, with their
names, layouts, paddings and rules. It watches the file, and when it changes
applies it again to the windows it has. It also sets what a window that opens
gets (window_container_behaviour, float_override, floating_layer_behaviour,
for a workspace or for all of them) and what focusing does in monocle
(monocle_focus_behaviour). A key that komorebi for KDE does not read
is ignored, so one file serves every platform. Rules for particular applications
can be in files of their own, which app_specific_configuration_path names.
workspace-layout-rule makes a
workspace change layout with the number of windows it has, as the file's
layout_rules do, and generate-static-config prints the settings the daemon
has as a komorebi.json. The Rust types that read the file describe it as a JSON Schema
(schema.json, written by just jsonschema and printed by static-config-schema), and the
module's config option is typed from that schema (nix/komorebi-options.nix, written by
just nixgen), so a configuration written in Nix is checked when it is evaluated.
A program such as a status bar can subscribe to the daemon's notifications: it listens on a socket of its own, and after each event and each message, the daemon connects to it and writes the event or the message with the state of the window manager.
On NixOS, the module nix/nixos-module.nix runs the
daemon as a systemd user unit that the Plasma session starts, and systemd
starts it again if it fails.
Documentation
The user guide starts at docs/index.md,
docs/installation.md says how to install komorebi,
docs/usage/configuration.md explains komorebi.json,
docs/example-configurations.md has an example of it,
and docs/cli has a page for every komorebic subcommand, which
just docgen writes from the subcommands' help.
cargo doc --workspace --no-deps --open builds the API documentation.
CONTRIBUTING.md says how to work on the code.
docs/dev/ARCHITECTURE.md describes the model,
docs/dev/DESIGN.md says why it is built that way,
docs/dev/PLATFORM.md what it needs from KDE Plasma,
and docs/dev/vm-tests.md how the VM tests work.
License
komorebi for KDE is under the Komorebi License. Contributions
are accepted under the terms in CONTRIBUTING.md.