Design
The goal is smooth native tiling inside KWin — not another script hitting the script API ceiling — without maintaining a renamed compositor fork.
Patch, don’t fork
Section titled “Patch, don’t fork”We patch stock kdePackages.kwin:
- New code lives as normal source files under
pkgs/kwin-tiling/src/and is copied into the KWin tree at build time. - Existing KWin files are changed through a small
hooks.patch(~900 lines, +468 / −38 net) — CMake wiring, input hooks, workspace/window integration.
That keeps the compositor binary, protocol support, multi-monitor handling, and Plasma integration identical to upstream. When KDE fixes a bug in KWin, you get it automatically. On a nixpkgs bump you only re-test the patch surface, not an entire fork.
Layout engines set relative geometry on KWin’s CustomTile tree. KWin’s own
Tile / TileManager machinery handles gaps, window geometry, and rendering —
we add layout logic on top of infrastructure that already exists.
For rounded corners and focus outlines on frameless windows, use the separate
kde-rounded-corners
effect — a normal KWin plugin, not baked into the compositor.
Compared to KineticWE
Section titled “Compared to KineticWE”Early ideas came from theblackdon/kineticwe, a fork that proved native tiling could live inside KWin. We ported concepts and features from that work — little of its code remains here — and took a much slimmer integration path instead of carrying the fork.
| KineticWE fork | kwin-tiling | |
|---|---|---|
| Compositor to maintain | entire KWin tree (~3,300 tracked files) | stock kdePackages.kwin |
| Integration surface | 123 src/ files diverge from upstream KWin today |
37 files (22 new + 15 hooked) |
| Changes to existing KWin | baked into the fork (hard to isolate) | +468 / −38 lines in hooks.patch |
| Extra workarounds | ~20-file QPainter render path (~1.9k LOC), hand-rolled borders/rounded corners (~500 LOC), distro install scripts (~2k LOC), kineticwe binary rename |
rounded corners via a separate KWin effect plugin |
| Plasma bumps | rebase the whole compositor | re-test the patch surface |
The tiling hooks overlap heavily — 14 of our 15 hooked files are among the
changes KineticWE made — but the fork also touches 109 other src/ files
(render backends, OpenGL, plugins, scene graph) that we don’t need. Because we
extend KWin instead of maintaining a parallel compositor, there is no second
render path, no binary rename, and no per-distro install scaffolding.
What we dropped from the fork
Section titled “What we dropped from the fork”- Binary rename (
kineticwe→ stockkwin_wayland) - Distro install scripts and custom packaging hacks
- Hand-rolled window borders and built-in rounded corners
- QPainter backend workaround (~20 files) — we use KWin’s existing render path
What we kept or extended
Section titled “What we kept or extended”Ideas and features ported from KineticWE, reimplemented on our slimmer base:
- Master-stack, stacked, and centred layout engines (plus scrolling, which we added)
- Native
TilingControllerintegration with KWin’s move/resize/desktop signals - Float/ignore rules, gaps, and the settings KCM
- Cross-monitor move robustness and mouse resize inside columns
Rounded corners and focus outlines stay in
kde-rounded-corners —
the same pattern everywhere: extend KWin, don’t fork it.
Compared to kwilt
Section titled “Compared to kwilt”jtekk1/kwilt is a KWin script (KWin’s QJSEngine scripting API), not a compositor patch — a different category of project, and a useful look at the script-API ceiling this project exists to get past. Its model is a flat per-(output, desktop) window queue where only the last N windows are visible and the rest are minimized (“knocked out”); config is read once at script load, and there’s no persistence or test suite.
We evaluated it for ideas, not architecture — most of it doesn’t transfer to a
compiled CustomTile-tree engine, and a few things here are already more
capable (per-app float rules and per-(output, desktop) layout memory are
persisted; kwilt’s reset every script reload). What’s worth taking: a sticky
master-window pin, a focus-last shortcut, optionally hiding decorations on
tiled windows, and its autoGrid layout — a smoothly-interpolating
2×2→2×3→3×3→3×4 grid — as a new layout kind. There’s also one correctness
lesson from its resize handling: kwilt snapshots geometry at drag-start and
only updates a split when that specific edge moved; ours currently recomputes
both master ratio and per-window height weight from the final geometry on
every resize, which does unnecessary work and can drift the persisted ratio.
What we’re not taking: kwilt’s bounded-visible-window model (dual and
monocle knock out every window past a cap). This project always tiles every
window on screen — importing a minimize-on-overflow mode would fight the
smart-gaps logic and change what “tiled” means here.
See pkgs/kwin-tiling/README.md’s “Planned: kwilt-inspired improvements” for
the implementation plan.
For packaging details and source layout, see pkgs/kwin-tiling/README.md in the
repository.