# Graphics and recovery verification Run from the checkout. Tools are temporary Nix development dependencies; no persistent host installation is required. The two-guest smoke test needs access to `/dev/kvm` and creates its own headless Wayland display, VM disks and sockets under a fresh `/tmp/hydra-smoke-*` directory. It never uses the inherited display, active TTY, existing guests or existing disks. Evidence remains in that directory; the script stops only its own verified QEMU instances on exit. ```sh python3 -m unittest discover -s tests -v nix-build dev/tools.nix -A viewer --no-out-link nix-build dev/tools.nix -A qemu --no-out-link nix-shell dev/tools.nix --run 'python3 dev/smoke.py' ``` The smoke test builds a private input helper with an ordinary evdev/pc105 US keyboard map. It boots two tiny counter workloads from separate scratch qcow2 disks, connects two fullscreen viewers, and checks reserved switching/recovery, manual USB selection, explicit reconnect and independent QMP lifecycles. Guest memory counters must advance and QEMU PIDs must remain unchanged through switching. Viewer protocol logs must show no reconnection during switching. A test-only `ToggleKeybinds` action and F12 marker establish native labwc keybinding inhibition before checking the production reserved bindings. Those extra controls are absent from the production session configuration. See the [labwc configuration manual](https://labwc.github.io/labwc-config.5.html) for `overrideInhibition` and the [actions manual](https://labwc.github.io/labwc-actions.5.html) for native `ToggleKeybinds` behavior. USB acceptance checks the actual selector window title, holds the modifiers through opening, releases them inside the modal dialog, then switches again. QEMU's USB device list must stay unchanged without a selection. Both viewer USB filters deny automatic hotplug and connection redirection. This check does not attach a physical USB device or prove guest driver behavior. The viewer adaptation handles only the configured manual USB accelerator before forwarding an event to the guest. Upstream keyboard grabs remove the GTK application accelerator group; without this adaptation, the configured USB shortcut reaches the viewer but does not open its selector. ## Installed guest workflow `--iso` is optional. Attach or detach media only while the VM is stopped: ```sh bin/hydra add guest --iso /absolute/path/installer.iso bin/hydra start guest bin/hydra view guest # Shut down from inside the guest, then check it is stopped. bin/hydra status guest bin/hydra media guest --detach bin/hydra start guest ``` For an existing stopped guest, `bin/hydra media guest --iso PATH` attaches installation/rescue media. A missing configured ISO prevents a new start with an actionable error; it does not prevent listing profiles, status, stopping a running guest, reconnecting a viewer or detaching the missing media. Media updates share the VM lifecycle lock and preserve its qcow2 disk. Abrupt stopping still requires `bin/hydra stop guest --force` and verified QMP identity. ## Display preferences and EDID New developer profiles use user networking, 1280×800 and `refresh_millihz=0`. Zero omits the patched QEMU refresh property and remains compatible with stock QEMU. Hydra's installed defaults can use this flat `/etc/hydra/defaults.toml`: ```toml network = "bridge" bridge = "hydrabr0" width = 1920 height = 1080 refresh_millihz = 144000 ``` Only `add` reads distribution defaults. Explicit `--network`, `--bridge`, `--width`, `--height` and `--refresh-millihz` options win; existing profiles do not change. `--defaults PATH` selects another preferences file for development. Missing defaults use developer preferences; unknown or invalid fields fail before creating a profile. Dimensions are integers from 1 to 16384, refresh is an integer from 0 to 1000000 mHz. Nonzero refresh requires the tracked patched QEMU provided by `dev/tools.nix` and distribution packaging. The patch keeps the requested refresh through frontend resizing and extends EDID monitor ranges to include the generated preferred mode. To verify the actual patched QEMU generator with the pinned source archive: ```sh qemu_source=$(nix-build dev/tools.nix -A qemu.src --no-out-link) nix-shell dev/tools.nix --run "python3 dev/check-edid.py '$qemu_source'" ``` The probe applies the tracked patch to four relevant source files in a temporary directory, compiles the actual EDID and virtio GPU generator functions with a small dependency harness, and decodes the result. It checks 60/75 Hz fallback, 144 Hz configuration, a conflicting 60 Hz frontend request, resized dimensions, checksums and monitor ranges. At 1920×1080/144000 mHz the preferred timing is 143.997231 Hz after EDID clock rounding. This is an advertised guest mode, not measured frame delivery or proof of a 144 Hz physical output. ## Still requiring a dedicated physical session - Physical reserved chords, brightness/backlight permissions and touchpad input. - Physical held keys/modifiers across guest switching, grabs and modal dialogs; guest key-up delivery, pointer grab release and absence of stuck modifiers. - Physical USB selection/redirection and guest drivers; no automatic redirection. - Toolbar behavior at physical screen edges and actual recovery shell usability. - ASUS output selection at 1920×1080/144 Hz, guest-reported display mode, delivered frame rate, hidden guest graphics continuity, tearing and switching latency. Headless checks establish CPU workload continuity, protocol focus transitions and the exercised compositor actions. They cannot prove physical input or scanout.