mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-15 13:22:19 +01:00
00aec79ee44767aa573f5eda67c8b8eedf394223
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1ee2405e44 |
docs(repo): trying komorebi in a virtual machine
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. |
||
|
|
e37f275b9b |
feat(nix): run-kde, the command
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. |
||
|
|
9b60a0020d |
chore(scripts): probe a WSL2 NixOS VM for KVM, WSLg and RDP
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. |
||
|
|
bab8f35234 |
ci(github): a weekly canary against the tip of nixpkgs
komorebi follows the tip of nixpkgs-unstable, so KWin and Plasma move under it each time flake.lock does, and the pull request that moves the lock runs the slow VM tier only when it is labeled. The `canary` job of ci.yaml, on the weekly schedule and on a manual run, copies the checkout, runs `nix flake update nixpkgs` in the copy and builds every check against the result: the checks that need no virtual machine, listed as the lint job lists them, then the suites of both tiers, listed from `vmTiers` as the discover job does. They run one after the other on one KVM runner, so that komorebi and the guests are built once against the new nixpkgs; the job may take the six hours a hosted runner allows. When a check fails, the job opens an issue titled "Canary: a check fails against the tip of nixpkgs-unstable" with the failed checks, the nixpkgs revision, and the KWin version at the tip and in flake.lock, or comments on that issue if it is open. The same report goes to the job summary on every run. The job's token can read the contents and write issues, nothing more, and the checkout keeps no credentials, so the repository is never changed. The composite action takes `cache: "false"`, which the canary passes. The store cache is keyed on flake.lock: what it restored would be the old nixpkgs, taking disk the guests need, and what it saved would replace the store of the jobs that do use the lock. `just lint` runs actionlint, with shellcheck over the scripts, both from the nixpkgs flake.lock pins, so the lint job and `just check` check the workflows. The steps were run as written on an x86_64 machine with KVM, against a fake gh: GitHub Actions has not run the job yet. CONTRIBUTING.md, docs/dev/vm-tests.md and DESIGN.md say what it does and why. |
||
|
|
3a3a0f8f24 |
feat(wm): JSON schemas, and the module's typed options
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. |
||
|
|
6f657e53e3 |
feat(cli): komorebic
The daemon answers on its socket, but a person has no way to write a message on it. komorebic is that way: each subcommand that asks something of the daemon is one message, sent with komorebi-client, and a question's answer is printed. komorebic is a separate program because the daemon takes no input but its socket, so a key binding, a script and a terminal all drive it the same way. It has what the daemon can answer so far: state, monitor-information, query, retile and stop. Around them, start runs the komorebi on the PATH in the background and passes it what follows a --, such as --fake-backend to try it without a compositor; log follows the daemon's log, which the daemon now writes to a file a day in LOG_DIR as well as to stdout; data-directory shows where the socket is; completions prints a shell completion script. The hidden docgen writes each subcommand's help to docs/cli, in fences that name their language and wrapped at 100 columns whatever the width of the terminal, so that the pages are the same wherever they are generated; just docgen runs it. The user guide starts here: docs/index.md for KDE Plasma, and a page on running komorebi. tests/cli.rs runs each subcommand against a socket the test serves in place of the daemon. In the VM tests the guests get komorebic next to the daemon, and vm-core-e2e reads the daemon's state with it (the windows it manages, the one it focuses, after KWin restarts too) and stops it with komorebic stop, which unloads the bridge and removes the socket. In the VM test library, Kde.state asks again, a second (times the scale) after an empty answer or a failed command, up to 10 times. The daemon drops a query when it cannot take hold of the window manager within a second, and komorebic then prints an empty line and succeeds; komorebic itself gives the daemon a second to answer and fails when that is not enough. A daemon busy with the first windows does either on a machine that is loaded or slow, and a suite that polled the state died on JSONDecodeError. An answer that is not JSON still fails at once. |
||
|
|
15ec3c1274 |
feat(kwin): read the desktop through a script in KWin
KWin has no protocol that lets another process read or set the geometry of a window, but its scripting API has both, and a signal for every change the window manager follows. So the KWin backend is in two parts: bridge.js, a script the daemon loads into KWin, which reports what KWin sees and holds no window manager logic, and a D-Bus service in the daemon, org.komorebi.Bridge, which the script calls with every event as a JSON string. Every decision about what an event means is made by the table in kwin::state, which is pure and is unit-tested with the JSON the script sends: which windows may be managed, the ids komorebi gives KWin's windows, and the Show, Destroy, FocusChange, Minimize and Restore events of the window manager. Events reach the window manager in the order KWin raised them: the Event interface handles its calls inline (zbus reordered them otherwise), and one channel and one thread, window_manager_event_listener and listen_for_events, carry them on. Commands go to the script as the answer to a Poll call that the script always has open and the daemon holds until it has something to send: in the spikes this was as fast as any other channel, and it makes one call every 10 seconds when nothing happens. Here the only command is the cursor query. Every command of the Backend trait is refused with "the KWin backend only reads the desktop", so the daemon follows KWin and moves nothing. The bridge is JavaScript that KWin runs as it is, and it gets types and tests without a JavaScript build chain. tsc checks it as strict TypeScript through JSDoc types of every message, against kwin-api.d.ts, a declaration of the part of KWin's scripting API it uses, in which every property is read-only and callDBus takes one string. bridge.test.js runs it under node --test against a fake KWin in a node:vm context. The messages of a session are JSON fixtures that both sides' tests read: the bridge's tests check that it sends those events, and the table's and the backend's tests read the same events and commands, so a message cannot change on one side alone. just check-js and the flake's komorebi-bridge-js check run both, with nodejs and typescript from nixpkgs; there is no package.json. The daemon now connects to KWin by default, and --fake-backend is hidden. The VM suite vm-kwin-events starts it against a headless KWin and checks, from its debug log, that the script loads and reports what was open, that KWin's signals arrive as events and in order, and that stopping the daemon unloads the script. docs/dev/PLATFORM.md starts with what komorebi needs from Plasma. zbus 5's zvariant, zbus_names and zvariant_derive are flagged unmaintained, since their code moved into zbus 6; deny.toml ignores the three advisories. |
||
|
|
3c51b9b4bf |
build(nix): a NixOS test framework for headless KWin
The unit tests run against a fake desktop and cannot say whether KWin does what the daemon asks. This adds the other half: NixOS machines with Plasma in them, a real KWin, and a Python library to drive it. Every directory under `nix/tests/vm/suites` is a test. The generator lists the directory, so a new suite appears as `checks.<system>.vm-NAME` on Linux and as `legacyPackages.<system>.vm.NAME` everywhere, with no edit to the flake. A suite names its tier and its node and has a `test.py`; `legacyPackages.<system>.vmTiers` lists the names of each tier as JSON for the CI matrix, `vmGuests` the guests to build on a Linux builder, and `vmTcg` the same suites for a machine with no KVM. `scale` multiplies every wait: 1 for KVM, 1.5 for HVF, 8 for TCG. The headless node runs `kwin_wayland --virtual` as user units of a lingering user, one pair of units per session (`session-bus@NAME`, `kwin-virtual@NAME`), so a test can run several sessions with their own bus and socket. A virtual KWin has no output to draw on, so anything that needs pixels or a pointer needs the session node. The library, `komorebi_vm`, has `Checks` (counted checks that fail once, at the end) and `Kde` (start a session, run clients and tools inside it, read windows through kdotool, poll with `wait_for`). It depends on a small `Machine` protocol rather than on the test driver. The `vm-lib` check runs its unit tests against a fake machine, with ruff and ty. The driver type checks each `test.py` with ty, which is how a protocol that did not match `QemuMachine.execute` was caught. `vm-kwin` starts a KWin with two outputs, spawns a foot window, reads it back, starts a second session and checks that it cannot see the first. On a Mac the guest is built on a Linux builder and the driver run directly, because the macOS nix sandbox cannot give QEMU the hypervisor: `scripts/vm-mac`, `just vm NAME`. docs/dev/vm-tests.md covers this, adding a test, and the builder. |
||
|
|
426409fcfc |
chore(repo): a workspace of two crates
komorebi for KDE is built up as a Cargo workspace with one crate per concern, the way komorebi and komorebi for Mac are. The first two crates are the `komorebi` daemon, which does nothing yet but report its version, and `version`, which owns the string every binary of the workspace reports. `version` reads the commit from git when HEAD is on a branch, and from COMMIT_HASH otherwise. A jj workspace leaves HEAD detached, so `.cargo/config.toml` defaults the variable to `unknown` (nix will set the real commit), and a plain `cargo build` in a jj checkout compiles. The tooling files come from komorebi for Mac: the licence, deny.toml (trimmed to the licences in use), rustfmt.toml and the toolchain file. `just check` runs the formatting check, clippy and rustdoc with warnings denied, and the tests. rustfmt.toml uses an unstable option, so formatting needs a nightly rustfmt. The `rg` calls in `just lint` name a path because rg reads stdin when it is given none and stdin is not a terminal. The README and CONTRIBUTING.md say how to run, check and commit. |