evidence · 12.2 KiB

README.md

Committed at docs/evidence/home-screen/qemu/README.md. This is what grounds the requirements that cite it.

Open raw source

Polished pinned Home screen: headless QEMU injected-touch acceptance

Observed 2026-09-24, source branch fix/home-screen-polish, commit 084d550c (rebased onto origin/master’s 9ff0462c; the change this polishes is openspec/changes/the-shell-presents-a-pinned-home-screen/, itself based on c575479b). Superseded capture of the same suite: a first cut of this same test (icons capped at 60px, no wallpaper, flat-color fixture icons, a black dock bar) is described in this file’s own prior revision; every observation below is from the redesigned rendering (home_grid.rs, home_screen.rs, render.rs).

This ran the cross-built Sway with the card-shell patch (/nix/store/cwpfizab7mz285vcyha1kci64ij2m511-sway-unwrapped-riscv64-unknown-linux-gnu-1.12/bin/sway, found via nix-store -qR on .#card-shell) and the Rust shell client (/nix/store/cx1linpyyr7xdib4r6n6ay1ry8lnhbla-k230-shell-rust-riscv64-unknown-linux-gnu-0.1.0/bin/k230-shell-rust) under qemu-riscv64-static with a 568x1232 headless Pixman output. The compositor’s native card_shell test-touch route supplied synthetic Wayland touch events (SWAY_K230_CARD_SHELL=1 SWAY_K230_CARD_TOUCH_FIRST=1 SWAY_K230_CARD_TEST_INPUT=1, matching the real integrated session).

Real theming and icons, not fixtures. Unlike the first cut, this run uses:

Reproduce with the committed runner and exact executables:

python3 tests/rust_home_screen_qemu.py \
  --sway /nix/store/cwpfizab7mz285vcyha1kci64ij2m511-sway-unwrapped-riscv64-unknown-linux-gnu-1.12/bin/sway \
  --swaymsg /nix/store/cwpfizab7mz285vcyha1kci64ij2m511-sway-unwrapped-riscv64-unknown-linux-gnu-1.12/bin/swaymsg \
  --rust /nix/store/cx1linpyyr7xdib4r6n6ay1ry8lnhbla-k230-shell-rust-riscv64-unknown-linux-gnu-0.1.0/bin/k230-shell-rust \
  --theme-bundle /nix/store/2idwl586jkz327w3rypg1mfw41pmbgrj-handheld-theme-default-28ceaae7 \
  --icons /nix/store/in3nc21zx02xiz2rdp5bpq36719x3axd-handheld-theme-icons-25.10.3 \
  --output /tmp/k230-hp-N

PASS: all eighteen checks in result.json are true.

The layout, and why

home_grid.rs now sizes a 4-column x 5-row grid (20 icons per page) plus a 4-slot dock — 24 icons resident at once, matching icon.rs::CACHE_LIMIT exactly, so a fully populated page plus dock never evicts and re-decodes an icon mid-frame. 4 columns matches the drawer’s own reasoning (a Home tile is icon-first, unlike the drawer’s name-first list); 5 rows is what this panel’s actual vertical budget yields once a compositor-imposed top-edge gesture band (see below), the page dots, and the dock are reserved, while still landing each icon’s glyph at 80px (grid) / 88px (dock) — both inside the 72-96px “sized for a finger” band — with a rounded “squircle” plate, generous tile spacing, and the icon+label block centered in its row rather than pinned to the row’s top edge (the first cut’s “crammed at the top, empty below” look).

The dock is a translucent themed panel (service_card’s own “launcher” brushes, the same theming every other floating panel in this shell uses), holding its own larger, unlabeled icon plates — webOS Quick Launch / Android hotseat convention — not a flat solid-color bar. Page dots are a small pill for the active page and plain dots otherwise. Every label gets a soft drop-shadow pass (shadowed_label) for legibility over an arbitrary wallpaper, since Home (unlike every other panel here) paints directly over it with no opaque backing.

Rearrange mode now shows a small red remove badge (iOS/webOS jiggle-mode style) at each filled icon’s corner — tapping it removes that icon immediately, no drag required — alongside the original drag-to-the-Remove- pill flow, which still works unchanged. A dragged icon’s live drop target is now highlighted (a translucent rounded rect over the cell it would land on).

A real compositor-level finding, not just cosmetic: under SWAY_K230_CARD_TOUCH_FIRST=1 (set for every real session, nix/shell.nix), the card-shell compositor patch consumes any fresh touch-down with y < 48 for its own pull-down-for-shade gesture, before any client surface ever sees it (card-shell-policy.c’s default edge_band = 48, adapter.c’s touch_first() branch). The first draft of this redesign put the Done/Remove pills at y: 16-72, which a drag release could still reach (a touch’s owning surface is fixed at its down point) but a plain tap could not — confirmed by first reproducing the failure directly against this real compositor via headless touch injection, then fixing it: home_grid.rs’s pills now sit at y >= 52, with a new unit test (done_and_remove_pills_clear_the_compositors_top_edge_gesture_band) pinning the constraint in code, not just tribal knowledge.

What each capture shows

Sparse (2-4 app) interactive fixture, dark theme, real wallpaper/icons:

Same fixture, light theme (catppuccin-latte, prepared live, real 1-color-fade.webp wallpaper):

Showcase pass — a fully populated, single-page Home (not the interactive fixture’s deliberately sparse 2-4 apps), captured at rest only, one page exactly filling the 4x5 grid plus a 4-icon dock:

Limits retained

This proves headless QEMU compositor/client wiring, layout, theming, and one real compositor-level touch-routing constraint (the edge-band finding above) with injected touch. It does not prove a real finger, panel optics, daylight readability, or physical long-press timing feel. Real-finger board acceptance remains an open task in openspec/changes/the-shell-presents-a-pinned-home-screen/tasks.md.

A prior run of the first-cut fixture found icon painting always fell back to an initial-letter plate because the loaded appearance snapshot’s own icon_theme field overrides whatever K230_ICON_THEME the process started with; this run relies on that same mechanism deliberately (the bundled/ prepared generation’s own icon_theme, Yaru-purple/Yaru-blue, is what selects the real icon theme, so K230_ICON_THEME is not set at all here). A second, unrelated prior run intermittently captured a solid black frame for a pass’s very first screenshot, since ready-idle only proves the process logged its own startup line, not that the compositor painted a first real frame; this run hit a variant of the same race specifically in the 24-icon-heavy showcase pass (icon decode outlasting the ordinary settle wait) and adds an explicit longer settle plus a same-as-committed guard check (showcase_*_is_not_a_blank_frame) so a recurrence fails loudly instead of silently shipping a black screenshot.