# quibble **quibble** is a semi-headless Wayland compositor built for automated testing of graphical programs. It hosts Wayland clients without needing a GPU or a display: by default it composites entirely in software into an in-memory framebuffer, so it runs unchanged in CI and other non-graphical environments. It opens no window of its own and processes no interactive input — a test run drives everything remotely. The compositor exposes a **control socket**. A companion CLI, `quibblectl` (or your own client speaking the same protocol), connects to it to take screenshots, inject pointer/keyboard/touch input, list and arrange windows, block until a window appears, read and write the clipboard, and resize the virtual output. Compositing defaults to pure software (pixman), which also makes screenshots deterministic across machines; `--gpu` switches it to OpenGL ES on a real card for clients that need hardware acceleration. "Semi-headless" means the graphical extras are opt-in and degrade gracefully. When a display server is available, quibble can open a **monitor window** that mirrors the framebuffer so a human can watch a test in progress. When a system tray is available, it shows a **tray icon** whose menu toggles that window and can quit the compositor (a left-click on the icon toggles the monitor too). If neither is present, quibble just runs headless and says so. Those two are independent: a session bus is enough for the icon, so it can appear where no display server will have it. When a click finds the monitor window impossible, the icon switches to a warning and puts the reason on hover rather than doing nothing. Both features need quibble itself to be able to reach the surrounding session, so a launcher should leave `WAYLAND_DISPLAY` and `DISPLAY` alone rather than clearing them to keep the client in. Confining the client is quibble's job: a client it launches gets `WAYLAND_DISPLAY` pointed at quibble's socket, with `WAYLAND_SOCKET` and `DISPLAY` removed so nothing can reach past it to the real desktop. To cover the two dominant compositor families, quibble supports two layout policies, switchable at runtime: **tiled** (one near-fullscreen window at a time, with popups floating on top) and **floating** (freely overlapping windows that can be moved and resized individually). ## Quick start ```sh # Build both binaries. cargo build --release # Start the compositor. It prints the two things clients and tools need: # WAYLAND_DISPLAY=wayland-2 # QUIBBLE_SOCKET=/run/user/1000/quibble-wayland-2.sock ./target/release/quibble & # Point a client at it (in another shell, or let quibble launch one for you # with `quibble -- weston-terminal`). WAYLAND_DISPLAY=wayland-2 weston-terminal & # Drive the session with quibblectl. It finds the socket via $QUIBBLE_SOCKET # or $WAYLAND_DISPLAY, or takes an explicit --socket. export QUIBBLE_SOCKET=/run/user/1000/quibble-wayland-2.sock quibblectl wait-window --app-id org.freedesktop.weston.terminal quibblectl list-windows quibblectl click 640 400 quibblectl type 'echo hello' quibblectl screenshot -o shot.png ``` ## Remote-control capabilities `quibblectl` mirrors the wire protocol one subcommand per request. Highlights: - **Screenshots** — `screenshot [-o file] [--region X,Y,W,H]` (PNG). - **Input injection** — `pointer-move`, `click`, `pointer-button`, `axis`, `key`, `type`, and `touch-*`. - **Window introspection** — `list-windows`, and `wait-window` which blocks until a matching window maps (removing races from test scripts). - **Window/layout control** — `focus`, `move`, `resize`, `layout tiled|floating`. - **Output** — `set-output [--scale s]` to resize/rescale the virtual screen at runtime. - **GPU** — start with `--gpu` to composite on real hardware and let clients do the same; see [GPU-rendering clients](#gpu-rendering-clients-opengl-vulkan-wgpu-egui). - **Clipboard** — `clipboard-get` / `clipboard-set` for testing copy/paste. - **Session** — `monitor on|off`, `ping`, `quit`. Run `quibble --help` and `quibblectl --help` for the full reference. ## Client compatibility Beyond the core (xdg-shell, wl_shm, wl_seat, wl_output), quibble advertises the protocols real toolkits expect, so hosted apps render and behave faithfully: xdg-decoration (forced server-side, so windows are borderless and screenshots are chrome-free), viewporter, fractional-scale, primary selection, clipboard data-control (so tools like `wl-clipboard` work), presentation-time, cursor-shape, xdg-activation, relative-pointer, pointer-constraints (pointer locks are honored during input injection), and wlr-layer-shell (backgrounds, panels and overlays are composited in the correct order). With `--gpu`, it also advertises linux-dmabuf and linux-drm-syncobj (see below). XWayland is not supported yet. ### GPU-rendering clients (OpenGL, Vulkan, wgpu, egui) quibble has two compositing backends, chosen at startup. **Software (the default).** Compositing runs on the CPU with pixman and only shared-memory buffers are accepted, so a client that wants the GPU has to fall back to a software rasterizer: install Mesa's software drivers (the **`vulkan-swrast`** package, i.e. lavapipe) and select one if the client does not do so itself. ```sh LIBGL_ALWAYS_SOFTWARE=1 ./my-opengl-app VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.x86_64.json ./my-egui-app ``` **Hardware (`--gpu`).** Compositing moves to OpenGL ES on a real GPU, and quibble advertises `linux-dmabuf` and `linux-drm-syncobj-v1`. Clients render on the card and hand over dmabufs, which are imported as EGLImages. Screenshots still come back as ordinary PNGs — the composited framebuffer is read back from the GPU — so nothing about the control protocol changes. ```sh # Composite on the first render node in /dev/dri... quibble --gpu # ...or on a specific GPU. quibble --render-node /dev/dri/renderD129 ``` It is off by default on purpose. GPU-rendered content is not bit-identical across machines, and reproducible screenshots are the reason the software path exists. Turn it on for the tests that need acceleration, not globally. If a GPU is asked for and cannot be provided, quibble exits rather than falling back to software behind your back. Compositing on the GPU is what makes the hardware path trustworthy, not merely faster. The software renderer can import dmabufs too, but only with a linear modifier, which forces clients down an untiled path — and on the proprietary NVIDIA driver that path is broken: its Vulkan swapchain advertises `DRM_FORMAT_MOD_LINEAR` with a tight stride while filling the buffer with block-linear (GOB-swizzled) data, so windows composite as horizontal banding. Reproduced on an RTX 4080 with `vkcube`, `vkcubepp`, `vkgears` and a `wgpu` application. Importing as an EGLImage instead lets clients keep their native tiled modifiers and leaves the driver to interpret its own layout, so the question never arises. `--gpu` therefore selects the GL renderer rather than bolting dmabuf onto the software one, and `a_vulkan_client_composites_coherently` in the integration tests guards against the regression. ## Layout of the workspace - `quibble-proto/` — the JSON control protocol shared by both binaries. - `quibble/` — the compositor daemon. - `quibblectl/` — the command-line remote control. ## Building and testing ```sh cargo build # both binaries cargo test # integration tests drive the real binary over the socket ``` The integration tests launch the compositor and exercise the protocol end-to-end. Several drive real Wayland clients (`weston-simple-shm`, `swaybg`, `weston-simple-egl`, `vkcube`) and skip when those are not installed; the GPU tests additionally skip without a usable DRM render node, so the suite stays green on a headless CI machine. The pixman renderer needs the system `pixman` library available at build time. The GL renderer needs no build-time library — libEGL is loaded with `dlopen` — so a machine with no GPU drivers still builds everything and simply runs the software path.