Run once on a real KWin with every tile 200 pixels too wide, and not
committed: seven checks failed, all about tiles on each other, and the
other 139 passed.
A port that cannot start with a case's rules, or orders its windows
otherwise, is recorded as a known failure for that case, and the other
cases run on. kfw does not accept the place as a JSON number, so
BEHAVIOUR.md lists the case as a known failure there.
Where a window that opens goes, from the placement rules of the
config: none, Primary, a number as a number and as text, and rules by
title for the first and the last place. restart_with starts the daemon
afresh with a komorebi.json the scenario gives. KdePlatform.spawn
quotes the title, which a title with spaces needs. core-e2e runs it as
section 12a.
A window kfw had cloaked is uncloaked by the stop and managed again on
the first workspace. A minimized window is left minimized and is not
managed. The lifecycle scenario counts only the windows in view.
The pointer on kfw reaches the first display only, because QMP's
absolute tablet is mapped there. The commands sweep passed in the two
runs that had the host to themselves and failed in the two that ran
beside a VM suite and a build.
kfw's 0ffc502c fixes the resize shortfall: a 60 pixel drag widens the
left tile by exactly 60. The commands sweep passes with a second
display, and rules, hotplug and lifecycle pass on a Windows guest.
The earlier sweep failures came with a kfde suite running beside it.
Checks.known_failure records a check that fails for a reason that is
to be followed up on: the suite does not fail, the totals say how many
known failures there are, and one that holds fails the suite so the
marker goes. unknown_monitor takes the reason, and core-e2e runs it
last, because the window it loses stays lost. BEHAVIOUR.md says so.
After clear-all-workspace-rules a window that the old rules matched
opens on the workspace in view and stays there. The first window of
that title is closed before the next opens, because the title is how
a window is named on both ports.
The daemon stops, every window stays, and a daemon that starts again
manages the windows that were in view, tiled inside the work area and
clear of each other, and also a window opened afterwards. The fake
platform learns to stop and to start with no memory of workspaces.
core-e2e runs it before its final stop.
kfw declares the subcommand with arg_required_else_help, so without an
argument it prints help and exits 2, although the option has a default.
kfde accepts the same option, so the sweep passes it explicitly:
--implementation windows, the default on both ports.
The scenario sends the focused window to monitor 7 with send-to-monitor and
move-to-monitor, which no display has, and checks that the daemon still
manages every window. A port may refuse or ignore the command, but a window
that was managed has to stay so: one that is not is left on the screen with
nothing tiling it.
It found a bug in both ports. kfw managed one window fewer than the OS had
afterwards (seen by hand on a Windows guest), and kfde failed it in
core-e2e on a real KWin: move_container_to_monitor takes the focused
container out of its workspace before it looks the monitor up, so the
error for a monitor that is not there comes after the container is gone.
It is not fixed, so the scenario is not in core-e2e, which would fail;
BEHAVIOUR.md has the finding. Its unit tests run it against a fake that
loses the window and one that ignores the command.
The sweep has commands that name the second monitor, send-to-monitor 1 and
the ones after it, so it needs two displays. The KDE suite runs with two
outputs and never noticed. On Windows, with one display, the first run of
the sweep reported that after send-to-monitor 1 kfw managed one window
fewer than the OS had, and every command after it was reported unhealthy;
whether that is kfw losing a window sent to a monitor that does not exist
has not been confirmed by hand. The sweep now plugs in a display first if
the platform has one, and a fake with one display shows it does.
A table of the scenarios against the two ports, with where each result
comes from, the resize finding and its cause (kfw reads the rectangle a
drag started from when it handles the start event, about 100 ms after the
drag began, so the distance it measures is short by what the window moved
in that time, and more so for a longer drag), what the scenarios had to
allow for, and what is not covered.
commands is every layout, a sweep of 216 commands and the commands that
only print, each of which must be understood and leave the daemon healthy
(it answers state and manages the windows the OS has). A command the port
lacks, which komorebic says with 'unrecognized subcommand', is skipped with
its name, which is what a behaviour matrix is made of: Checks.skip records
it and finish prints how many. The commands suite runs it instead of its
own copy of the list: 270 checks pass, none skipped.
rules gives the daemon an initial rule, a rule that is not, and an ignore
rule, by title, and checks where each window goes. It found that
enforce-workspace-rules applies initial rules again on purpose, so the
scenario asks whether the daemon sends the window back on its own.
hotplug unplugs a display, plugs one in and sends a window to it, and
starts by plugging one in if the platform has only one. Platform gets
displays, add_display and remove_display, and KdePlatform does them with
kscreen-doctor. The hotplug suite runs it: 34 checks pass.
core-e2e runs rules after the others, then leaves a window for the stop to
leave behind: 130 checks pass on KVM. One run failed the check that the
daemon had exited after komorebic stop and the next did not; the cause is
not known, and the suite now prints the unit's status when it happens.
Snapshot.workspace_of tells which workspace has a window.
Seven of the eight shared scenarios pass on a Windows 11 guest under QEMU
against kfw's daemon, as the session that ran them reports. The eighth,
resize, ran there and found that the edge moves less than the pointer; an
unmanaged window followed the same drag exactly, so the shortfall is in
what kfw does with the drag.
monocle toggles monocle on the focused window and checks that it fills the
work area up to the layout's padding, that the state has it as the monocle
container, that every other window is put away, that it keeps the focus,
and that toggling back puts every window in view on the tile it had.
closing closes a window and checks that the OS has no frame for it, that
the daemon manages one window fewer, and that the windows left in view
take more room between them, inside the work area and clear of each other.
Platform.close closes a window as its button would; KdePlatform does it
with kdotool. The fake daemon models monocle and closing, and its
os_window now knows the workspace a window is on, which the workspaces
scenario had passed without. core-e2e runs both: 120 checks pass on KVM.
Platform.has says whether a platform can do what a scenario needs, and
Platform.pointer is a Pointer (move, down, up, drag) where it has one;
KdePlatform takes the pointer of a session node. scenarios.resize drags the
edge two tiles share by 60 pixels and checks that the tiles that end at the
edge are that much wider, the ones that start after it start that much
later and end where they did, nothing moves up or down, and tiles that do
not touch the edge are where they were. A platform with no pointer skips
it, and says so.
The fake daemon has a pointer that moves an edge. The scenario runs on KDE
at the end of drag-resize: 19 checks pass on KVM. Its first run failed one
check that was the scenario's: both tiles of a BSP column share the dragged
edge, and the check took the second for one that had not moved.
core-e2e writes the komorebic state of kfde, with three tiled windows and
with one stacked onto another, into its output, and the files are kept in
tests/fixtures beside kfw's three, recorded from a Windows guest (tiled,
stacked, one window floated). test_recorded.py reads all five with the
same checks: three containers of one window, a layout rectangle for each
inside the work area, each window where its container's rectangle is up to
a terminal cell (foot rounds to cells, kfw's test window does not), and a
stack that is a container of two whose focused window is the one the ring
names. Nothing in the parser had to change; the checks passed on the first
real kfw state.
The scenes written from the serializers stay for the shapes no run has
recorded. The sync script copies the fixtures too. The workspaces scenario
prints which windows are in view after the switch: one run of core-e2e
failed that check and passed on the next, and the log did not say which
window had been in view.
tiling has run against kfw's daemon on a Windows 11 guest under QEMU, 10 of
10 checks, with this core copied unchanged; the other four scenarios have
not run there yet. Says so in vm-tests.md, and what keeps the copy in
step.
scripts/sync-e2e-core copies komorebi_vm.core and the unit tests that
belong to it into another directory, and --check says whether the copy is
in step, so that kfw's Windows platform can be written against the code it
will run with while the move is not made. This repository is the source
until the move, and the script goes with it.
Two of those tests imported Rect and Checks from komorebi_vm, which pulls
in the KDE half; they import them from komorebi_vm.core now. The copy
passes its 25 tests in a directory with nothing but pytest and Pillow.
latency measured a p95 of 128.9 ms against its 100 ms times scale on the
WSL2 machine, under nested KVM with nothing else running, where x86-1 had
passed it at scale 1. Every Linux host is no faster than a Mac to the
suites that time the daemon, so scale is 1.5 on all of them: latency
passes at it with a p95 of 132.4 ms against 150 ms. The run-to-run
difference (p50 46.0 ms, then 70.0) is as large as the margin left, which
PLATFORM.md says.
PLATFORM.md records the wall times of the suites on this machine. All 33
were run there after the library was split: 32 passed at scale 1, and
latency is the one that failed. The other 32 were not rerun at 1.5, which
changes their derivations.
workspaces sends the focused window to workspace 1, checks that it is put
away and that the daemon has it there while the others stay in view,
switches to workspace 1 and checks that only that window is in view and has
the focus, and switches back.
hidden now says a window is out of sight for any reason the daemon has: on
KDE that is the parking desktop and also a desktop that is not the one in
view. Snapshot's workspace, containers and focused_title take the focused
workspace when none is given, and the fake daemon has two workspaces. The
scenario passes in core-e2e on KVM: 106 checks.
hidden says that a window exists but the daemon has put it away: the others
of a stack, here, and later those of another workspace or behind a monocle.
It is a window on the parking desktop on KDE and will be a cloaked one on
Windows. scenarios.stacks stacks the focused window onto its neighbor,
cycles the stack, checks that the window in view has the focus and fills
the tile the first had, and takes the stack apart.
The scenario's fake daemon now models stacks. Running it on KDE found a bug
in Snapshot: focused_title took the first window of the focused container,
and a stack's focused window is the one its ring names. Snapshot keeps that
index now. core-e2e runs the scenario after the others: 98 checks pass on
KVM.
focus moves the focus with focus and cycle-focus and checks that the OS and
the daemon's state agree on the window that has it and that no window moved.
moving swaps the focused window with its neighbor with move and puts every
window back with cycle-move, and checks that the focus stays with it. Both
read the windows through the Platform, so they are what kfw's suite will
run.
Their unit tests run against a fake daemon that follows the commands over
three tiles, and against two that do not (a move that moves nothing, a move
that loses the focus). core-e2e runs them after tiling on KDE: 85 checks
pass on KVM.
KdePlatform is a Kde session seen through the neutral vocabulary: kdotool
for the OS's frames, foot for clients, the cell of its font as the
tolerance. The core-e2e suite runs scenarios.tiling through it as a new
section, so that the scenario that will run on Windows is checked on the
one platform that works; the suite's own sections are unchanged. It passes
on KVM (74 checks) on the WSL2 machine.
A scenario drives the daemon and looks at the desktop only through a
Platform: start the daemon, komorebic, state as a Snapshot, spawn a window,
os_window (the OS's own account of its frame, which is the oracle: the
daemon's state is what is under test), work_area, activate and screenshot.
Only what a scenario uses is there; a method is added with the first
scenario that needs it.
scenarios.tiling opens windows and checks, against the OS's frames, that
each is where the layout says, inside the work area and clear of the
others. Its tests run it against a fake platform that tiles correctly and
against two that do not.
komorebic state prints a different JSON on kfde and on kfw: a window is
{id, rect, details} on one and {hwnd, title, exe, class, rect} on the
other, and a monitor and the state itself have fields the other lacks.
core.snapshot.parse reads either into monitors, workspaces, containers and
windows with the same names, keeping what only one has in Snapshot.extra,
so that a scenario does not have to care which port it runs on.
The fixtures in its tests are written from the two serializers, not
recorded; they are replaced by recorded states once a run on each port
exists.
Checks, Rect and the pixel helpers depend on no compositor. They move to
komorebi_vm.core, which may not import komorebi_vm.kde, so that the package
can go to the Windows port unchanged. komorebi_vm re-exports them and no
suite changes. This is the first step of the cross-platform e2e plan.
The lint job built every check that needs no virtual machine at once, and
the runner was shut down about six minutes in on both runs of 2026-10-28,
with nothing else failing before it. The checks are now a matrix of three
shards: the Rust workspace, the guests and their dry runs, and the rest.
A check that matches no other shard is built in the last one, so a new
one needs no edit. The lint job keeps just lint, and publish waits for
both.
A hosted runner of a private repository can land on a host with no
/dev/kvm, which failed one vm-fast job of a run (core-e2e, 2026-10-28)
and nothing else. The Enable KVM step now says so, and rerun-no-kvm
reruns the failed jobs, up to three attempts, when every failed job
failed on that step. A failed suite is not rerun.
zbus 6 splits the proxy and service parts of the crate into features, which
komorebi now asks for, makes the builders of connections, proxies and match
rules infallible until build(), and renames the zvariant aliases.
On a host whose GPU can encode H.264 (a Windows driver through d3d12 on WSL 2 does), KRDP in
the demo guest encoded on the guest's CPUs. The host side was fine (vainfo on the host lists
H.264 and HEVC EncSlice), and virgl can pass a guest's VA-API video to it, but a chain of seven
problems stood between the two. Each is a patch, and the guest takes them all:
QEMU (nix/dev/qemu-virgl-video.patch)
QEMU never asks virglrenderer for video: virglrenderer turns it on only for an embedder that
passes VIRGL_RENDERER_USE_VIDEO and a get_drm_fd callback to open the host's video device on.
The flag is defined only with VIRGL_RENDERER_UNSTABLE_APIS, which the build sets.
virglrenderer 1.3.0, four patches in this order
virglrenderer-chroma-format.patch
the guest's Mesa and virglrenderer number the chroma format of a codec differently: 4:2:0
was read as 4:2:2, and the host's encoder refused every codec it was asked for
virglrenderer-video-memory-upload.patch
the d3d12 driver has no dma-bufs: vaExportSurfaceHandle fails, nothing was uploaded and
the encoder returned an empty bitstream. The planes of the guest's picture are read back
(glReadPixels) and written to the surface through a derived image (vaDeriveImage, with
vaCreateImage and vaPutImage as a fallback)
virglrenderer-h264-packed-sps.patch
Mesa's VA-API takes the level of the stream from a packed SPS, which virglrenderer never
sent: level 0 reached UNREACHABLE in the d3d12 encoder and crashed QEMU
virglrenderer-ubo0-constants.patch
a constant buffer 0 bound as a buffer was never read by the shader, so the colour matrix
of an RGB to YUV conversion was zero and the picture was a solid green
Mesa 26.2.5 in the guest, two patches (hardware.graphics.package)
mesa-virgl-rgb-yuv-csc.patch
the graphics compositor never loaded an RGB to YUV matrix, and multiplied by the YUV to
RGB one of its initial state
mesa-virgl-fence-leak.patch
a surface kept the out-fence (a sync_file) of its last flush and the next flush stored a
new one over it: one file descriptor leaked per converted frame, 44 a second under motion,
and KRDP froze when it reached the limit of 1024, about 28 seconds in
KRDP (krdp.nix) asks for H264Baseline and the guest's driver lists Main and High, so ffmpeg's
h264_vaapi found no profile it could use: it is built with H264Main.
What was checked: a luma ramp and a colour-bar pattern encoded through the guest to the 4090
decode to the right values; the real desktop shows over RDP in Windows' own client with the
right colours; under es2gears motion the descriptors of krdpserver stay flat (3 sync_file, 78
of 1024) where they were 949 of 1024 after 28 seconds.
run-kde sets what the host needs for Mesa and for virglrenderer's libva to find the Windows
driver, on WSL 2 with the GPU on and unless it is set already: GALLIUM_DRIVER=d3d12,
LD_LIBRARY_PATH with /usr/lib/wsl/lib first, LIBVA_DRIVER_NAME=d3d12 and LIBVA_DRIVERS_PATH
(/run/opengl-driver/lib/dri, for the d3d12 video driver, which it looks for). --doctor checks
for the libraries and for that driver, and the tests have a scenario for each.
Known: the software decoder warns "illegal short term buffer state detected" on the stream; the
d3d12 encoder writes Constrained Baseline whatever profile is asked for. There is no binary
cache: the first run builds QEMU and Mesa, which takes about 20 minutes. QEMU, virglrenderer and
Mesa are the runner's and the guest's only, and the VM suites build the Mesa as well.
Not measured yet: whether it is smoother than the CPU encoder under the same load.
The guide for the one command: docs/trying-it.md says what a machine needs to
run the demo guest, with the lines of .wslconfig and of the NixOS-WSL
configuration for Windows 11, how to check it with `run-kde --doctor`, what the
command does and shows, the key bindings to try, how the GPU path is chosen, how
to open Windows' own RDP client, how long the first run takes (measured on a
machine with 32 cores and an empty store, by number of cores, with an estimate
for a WSL 2 machine and its caveats), how to start again, how to work on
komorebi in the guest, and what to do when something is wrong, by symptom. It
ends with what was run and what was not: nothing has been run on WSL 2 yet. The
README has a section and a link, and the user guide index a line.
DESIGN.md has four entries. Two record what I decided and want: the QEMU window
first and RDP second, and the GPU wherever the host has one. Two say that a
reason was not written down, for QEMU with KVM and not Hyper-V and for sharing
the password by a directory, and what is known that bears on each; the password
entry also says why a password is made for each user. PLATFORM.md has a section
for WSL 2 with what the command expects, a table of the probe results still to
be recorded (KVM, display, GPU, RDP, resources, virtiofs), and what is known of
virtiofsd without WSL 2.
The demo guest is what the command `run-kde` starts, so it should not stop
working between the times somebody runs it by hand. The suite `run-kde` puts the
guest module, nix/dev/run-kde.nix, on the session node, as the rdp suite does
with krdp.nix, and checks what only a booted session can say: the demo opens two
Konsole windows, Kate and Dolphin and komorebi tiles them clear of the panel,
the cheat sheet is a window that is not a tile, what it shows of `komorebic
check` has no error, and KWin says what it draws with.
The node makes the directory the host shares as the guest finds it once it has
mounted it: owned by a user the guest does not have, with a password and a
public key in it. The suite checks that the guest takes them as root before the
login, that ssh lets in that key and no other, that KRDP starts by itself and
listens, and that a client with the password is let in and shows the display.
The client of those RDP checks moves from the rdp suite into the library as
RdpClient, with unit tests against the fake machine, so that both suites run the
same checks; the machine protocol gains copy_from_machine for it.
Starting the demo guest by hand takes a build, a state directory, a password, a
key, the sizes of the machine and the right arguments for its display, and a
mistake shows up as an error from QEMU a minute later. `run-kde` does all of it:
`nix run .#run-kde` (or `just run-kde`) checks the machine, builds the guest
when its runner is not in the store, with the build log shown, makes the state
directory with a sparse disk, a random RDP password and an ssh key (all mode
0600, none of it in the Nix store), and starts the runner with KVM, the memory
and cores the machine has, the repository at /repo, and the state directory at
/run/run-kde. It prints a banner with the RDP address and password, the ssh
command, how to quit, and which graphics mode it chose and why.
`--doctor` prints every prerequisite as a tick, a cross or a warning with the
exact fix: WSL 2 or not, KVM and its group, nested virtualization, virtiofs (a
virtiofsd started as the user with the options of the guest's runner must serve
a directory, since the guest mounts its /nix/store through one; /dev/fuse is
only a warning), memory, disk, WSLg, Windows interop, the GPU and the OpenGL of
the host, and the lines of .wslconfig. It exits with 1 when something is a
cross, which includes a missing KVM unless `--tcg` allows software emulation.
The GPU is used when the display is a window, the host has a GPU device and its
OpenGL is not llvmpipe, and `--gpu on|off` overrides it; on WSL 2, `--rdp` opens
mstsc with a credential that lasts as long as the run.
The script reads the machine through variables, so `--dry-run` and `--doctor`
can be tested against machines that a check makes up: `run-kde-dry-run` and
`run-kde-doctor` compare their output with the texts in nix/tests/run-kde, and
the package, which shellchecks the script when it is built, is a check too.
A person who wants to see komorebi tile windows should not have to configure a
machine first. `run-kde-x86_64` and `run-kde-aarch64` are the session guest with
a demo on it: the NixOS module configures komorebi from a typed `config` (eight
workspaces, borders, animation) and the sample skhd bindings, and at login the
guest opens two Konsole windows, Kate and Dolphin, which komorebi tiles, and a
cheat sheet window that it leaves alone, with the key bindings, `komorebic`
commands to try, what KWin draws with, and the output of `komorebic check`.
The closure is the same for everyone. No password or key is in it: the host
shares a directory, which the guest mounts at /run/run-kde, and a unit copies
the RDP password and the ssh public key from it, since the files can belong to a
user of the host who is not the guest user. krdp.nix reads the password from the
file that `komorebi.krdp.passwordFile` names when it exists. The display device,
the port forwards, the memory and whether to insist on KVM are left to the
environment of the runner, so that one closure and one disk run with a software
virtio-vga or with a virtio-vga-gl that renders on the host GPU. The module is a
separate file that does not import plasma-vm.nix, so that a VM suite can put it
on the session node.
plasma-guests-eval asserts the same of the demo guests.
The Plasma guests were made for an Apple silicon Mac only: the flake fixed the
guest to aarch64-linux and the host of QEMU to the Mac. `mkPlasmaGuest` now
takes the system of the guest and the packages of the machine that runs QEMU,
and the flake has the two QEMU guests for a Linux machine with KVM as well:
plasma-vm-x86_64 and plasma-vm-session-x86_64, and the same pair for aarch64
(plasma-vm-aarch64, plasma-vm-session-aarch64). The three guests of a Mac keep
their names and are built from the same instructions.
An x86_64 machine has a VGA that QEMU adds unless it is told not to, which would
be a second display next to the virtio-gpu that KWin draws on. An x86_64 guest
therefore gets virtio-vga, a virtio-gpu that is also the VGA, and `-vga none`;
an aarch64 guest has no VGA and keeps virtio-gpu-pci.
plasma-guests-eval evaluates every guest and asserts what each is for: the
architecture of the guest, the system of its host, KVM or HVF, and one display
device. docs/dev/utm.md says which guest is for which host and how to build and
run one on a Linux machine; the README and CONTRIBUTING say that there are
guests for both.
The port can only be tried on a Windows machine, through a NixOS virtual machine
in WSL2, which the machines this history is built on do not have. What such a
machine can do for a Plasma guest is not known until something runs there, and
every guess about it is a risk for what comes next. wsl2-probe is that
something: one command, nix run .#wsl2-probe, that answers in about two minutes
what the guest needs to know and prints, for each answer that is not a yes, the
exact change: the .wslconfig lines, the NixOS option, the PowerShell command.
It checks the environment (WSL2, memory, disk, .wslconfig, Nix); KVM (/dev/kvm,
its group, and a tiny guest booted under KVM and under TCG, with the same loop
timed in both and on the machine itself, which must differ by several times);
the display (a QEMU GTK window through WSLg for a few seconds, and whether you
saw it); the GPU (/dev/dxg, the Windows driver libraries, the OpenGL renderer in
four environments, and virtio-vga-gl with gl=on); the Windows side (interop,
mstsc.exe, whether Windows reaches a port that WSL listens on); whether memory,
cores and disk are enough for the real guest; and virtiofs, which the real guest
uses for its /nix/store and every shared directory (/dev/fuse, virtiofsd as the
user with the options of nixpkgs' runner, and a guest that mounts the host's
/nix/store and a writable directory, reads, writes and compares). Each probe
runs apart from the others, so one that fails leaves the rest alone, and the
output ends in a block to paste back, which is also written to
wsl2-probe-report.txt.
The flake wraps the script with QEMU, virtiofsd, Mesa's eglinfo and glxinfo and
socat from nixpkgs, and builds the guest, a kernel from the binary cache, the
two modules of it that virtiofs needs and a static busybox initrd, as
wsl2-probe-guest, so nothing is compiled on the machine. Two checks keep the
script from rotting: wsl2-probe builds the wrapper, which runs ShellCheck over
the script and over the guest's init, and wsl2-probe-dry-run runs --dry-run in
the build sandbox, which has no KVM, no network and no display.
docs/dev/wsl2-probe.md says what each check means and how to read it.
cargo update moves two crates, regex from 1.12.3 to 1.13.1 and libz-sys from
1.1.30 to 1.1.31. winapi-util 0.1.11 keeps its version and now resolves to
windows-sys 0.61.2, which the lock already had, and not to 0.48.0. Cargo.toml
does not change, and neither do the git dependencies, which are pinned by
revision. A second cargo update finds nothing left to move.
Six crates stay where they are because their newer versions are semver-major and
need a Cargo.toml edit and code changes, which a lock update does not carry:
dirs 6.0.0 (7.0.0 is out), nanoid 0.4.0 (0.5.0), parking_lot 0.12.5 (0.13.0),
shadow-rs 1.7.1 (2.0.0), sysinfo 0.38.4 (0.39.6) and zbus 5.19.0 (6.1.0).
cargo deny check passes. It reports the seven advisories deny.toml already
ignores, all of them unmaintained crates and none a vulnerability, and no new
one. The gate is green on an aarch64 box and on x86-1: clippy with -D warnings,
nextest, doctests, rustdoc and the nix checks that need no VM, and udeps on the
aarch64 box.
All 32 VM suites pass on KVM on x86-1 at this commit, which is the tip of the
two bumps, rdp included. latency, which measures time, passed when it ran alone;
it failed once while another suite ran on the same host.
nixpkgs moves from d3b13ad (2026-10-24) to f05535a (2026-10-27) and rust-overlay
from c7e9d51 to 19ecdd0; the other six inputs have no newer revision. KWin,
Plasma and KRDP go from 6.8.1 to 6.8.2, so testedMax in nix/supported-kwin.nix
goes to 6.8.2, with its dated comment. Qt (6.12.0), FreeRDP (3.32.2) and the
glibc of nixpkgs (2.44) keep their versions. The stable Rust toolchain stays at
1.99.1; the nightly rustfmt moves from 2026-10-27 to 2026-10-28.
nixpkgs now has KDE Rounded Corners 0.11.0. nix/kde-rounded-corners.nix still
sets the version and the source, because its patch is written for the 0.11.0
source, so the package does not change: it builds with its patch, and Geometry
Change builds, against KWin 6.8.2. The dated sentences of README.md, DESIGN.md,
PLATFORM.md, COMPARE.md and of the docs of kwin/effects.rs say 2026-10-28 and
the new versions. Measurements taken on 6.8.1 keep their date and version, since
none was repeated on 6.8.2.
The glibc floor does not move: komorebi and komorebic still need 2.39 on aarch64
and on x86_64 (pidfd_getpid and pidfd_spawnp, both weak references), measured as
the build job measures it, before and after patchelf.
The gate with the nix checks passes on an aarch64 box and on x86-1,
nixos-module-eval among them (the KWin version scenarios included). The VM
suites run against the tip of these two commits; the result is in the message of
the next commit.
The Komorebi License lets anyone distribute the source code but not binaries, so
the flake has no public binary cache and the binaries of komorebi come only from
this repository. The `build` job builds `komorebi` and `komorebic` with nix on
every run, on ubuntu-24.04 and on ubuntu-24.04-arm, and keeps them as an
artifact for 14 days; the arm64 job is `continue-on-error`.
As nix builds them, the binaries load libc, libm and libgcc_s through the
dynamic loader of the store and an RPATH into it, which only a machine with the
same nixpkgs revision in its store has. The job points them at the system's
loader at its standard path and drops the RPATH, with patchelf from the nixpkgs
flake.lock pins, then runs `--version` on the Ubuntu runner to check that they
start. Built from the locked nixpkgs (d3b13ad), the newest glibc symbol version
they need is 2.39, the glibc of Ubuntu 24.04.
The `publish` job runs on a `v*` tag, and on a scheduled or manual run on
master, once lint, vm-fast and build have passed. It zips the two binaries of
each architecture as `komorebi-<version>-<target>.zip` (an architecture whose
build failed is left out with a warning), adds schema.json, the schema of
komorebi.json, when the repository has one (it does), and writes checksums.txt
with sha256sum. The release notes are kokai's, from the commit messages since
the previous version tag, as komorebi for Mac writes them; kokai is not in
nixpkgs, so cargo installs it, pinned at 0.3.1. A tag becomes a release. master
replaces the rolling `nightly` prerelease and its tag; the old nightly tag is
moved to the commit first, so that the notes list the commits since the last
version and not since the last nightly. There is no Homebrew, winget or second
repository.
The cache key of the composite action gets the runner's architecture: the arm64
runner has the same `runner.os` as the x86_64 one, and would restore and save
the other's store.
The steps were run as written on aarch64 and x86_64 machines: the builds, whose
binaries then started in Ubuntu 24.04 root filesystems of both architectures,
and a first nightly, a later nightly and a tag release against a fake gh. GitHub
Actions has not run them yet. CONTRIBUTING.md says how a release is made, the
README where binaries come from and what the license allows, and DESIGN.md why.
The pages that said there were no prebuilt binaries (README, installation.md and
the entry on the binary cache in DESIGN.md) now say that the releases are the
only ones.
docs/dev/COMPARE.md says what komorebi for KDE shares with komorebi for Windows
and komorebi for Mac, what differs, what KDE does by itself and what is not
supported, for a reader who knows komorebi on one of them. Its tables are
generated by scripts/compare-gen.py from this checkout and from checkouts of the
other two, because a count or a name typed by hand is wrong after the next
change to a command, a key or an event. The script also checks the notes that
say why a command has no counterpart, and the sentences of the document that
depend on a table, and stops when one of them is no longer true. CI has neither
checkout and cannot run it; CONTRIBUTING says when to run it. The tables have no
column for the commit that added a row: the plan that numbered the commits is
not in the repository, and a number the script cannot derive for the next commit
would stop it.
The other documents had places that said something the repository does not carry
or does not do:
- DESIGN cited docs/kde.md, which no commit carries, said spike 8 "asked
whether" one komorebi.json could be shared, and described the default of the
key binding mode as a plan. Each entry now states the rule and the evidence.
The first entry no longer says that the Windows implementation is the
reference: both it and the macOS one were read, and the native thing is
preferred where the other two need a workaround.
- "The spikes" was used in PLATFORM, DESIGN and COMPARE and defined nowhere.
PLATFORM's vocabulary defines it, and PLATFORM has a list of what was not
measured, which COMPARE links to.
- ARCHITECTURE said a maximized window is hidden with its workspace and shown
with it, and that the code has a TODO for cycling the focus. A workspace
switch hides nothing, since KWin shows and hides the desktop's windows, and
the cycle leaves a maximized window as it is; the TODO and the commented-out
code under it in window_manager.rs are replaced by a comment that says so. The
Key bindings section no longer repeats the headings of the sections before it.
- README listed the subcommands that work without the daemon, and the list was
short. It gives the rule and one example of each kind.
- The page on changing monitors opened with "nothing to configure" and then
named display_index_preferences.
- COMPARE said KWin waits 300 ms for the pointer to settle before it moves the
focus. That is KWin's default (DelayFocusInterval, in KWin 6.8.1), and
komorebi sets it to 0 while focus follows mouse is on; the page now says so.
- ARCHITECTURE's opening list of what the page describes stopped at the borders
and had no word of animation, themes, wallpapers, transparency, focus follows
mouse or key bindings. Two link definitions that it had twice are there once.
- PLATFORM's list of what was not measured has the progress of the stock
animation effect against the clock: one capture showed none four seconds into
a 30 s animation, where the curve gives about 60 %.
- README, docs/index.md and ARCHITECTURE link COMPARE.md.
- COMPARE's list of the methods of Backend has drag_end, which the end of a
resize is read through, and compare-gen.py has the row and the counts.