v2: Rust rewrite with a YAML grid layout, evdev gestures and a preflighting installer #1
Reference in New Issue
Block a user
Delete Branch "v2-rust"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Replaces the three shell scripts and lisgd with a single Rust binary. tmux stays the pane engine.
Deployed to the LattePanda rack display, which passed a physical swipe test and a cold boot.
The grid
Screens are placed at
"<row>x<col>"in a YAML config. Coordinates are sparse ordinals — only their sort order matters, so0x1and0x5are interchangeable and you never have to count how many screens a socktop group produces in order to place something beside it.A socktop group is one cell however many hosts it holds. Swiping forward walks its sub-sequence (tiled overview, then each host zoomed) and only leaves after the last one; coming back from the right lands on its last host so the row reads as one strip. Vertical movement returns to where you were in that row — snapping to the nearest column only decides the first entry.
Layout in service: unifly at
-1x0, two socktop groups across row 0, uptime-kuma-status at1x0.Bugs the deployment found
Four, three of which v1 also had and nobody had noticed.
Every swipe classified as up-to-down. X and Y arrive as separate evdev events, so using
(0, 0)as the "start not yet known" sentinel captured the start on the X event alone and recorded Y as zero. Displacement was then measured from the top edge of the panel rather than from the finger: a swipe at y=360 reported 360px of downward travel that never happened, which dwarfed dx. On the wall this took the display from the top row to the bottom row and stuck there, because "down" from the bottom row is a no-op — it looked like dead gestures while the process was healthy and the panel still grabbed. Start position is nowOptionper axis. The existing tests could not have caught this: they built((start),(end))tuples by hand and never ran the decoding, which is exactly where the bug lived. The slot bookkeeping is now aSlotTrackerfed by a small event enum, with six tests driving realistic protocol-B streams.tmux window options do not propagate from the session, and new windows do not inherit them.
set-option -t <session> pane-border-statusquietly applies to whichever window is current, so only one window ever got pane borders — the rack display has had unlabeled borders on two of its three windows since v1. Same trap that makesremain-on-exituseless here. Now set per window, with a test asserting every window has them.A program can overwrite its own pane label. unifly probes for Kitty graphics support on startup and tmux consumed part of the probe as an OSC title change, so the border read
Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA. Labels moved to a pane-scoped user option no escape sequence can reach.rundied if the panel was missing. The autostart can beat USB enumeration at boot. It now retries for ten seconds, and if the panel still is not there it says so loudly and keeps the dashboard up rather than replacing a display you cannot swipe with no display at all.Other notable
--xignoreis gone.EVIOCGRABtakes the panel exclusively so X never sees the touches, which is what theOption "Ignore"InputClass was faking — without the xorg.conf.d file or the relogin it needed.at: 0x0is hexadecimal 0 to YAML, and1x0is not valid hex, so only row-0 entries would break. Deserialization catches it and names the fix.set -elandmines fixed. Several[ test ] && actionlines abort the script when false, so a box with no lightdm, no i3 or nothing to install would have exited silently partway through detection — exactly the fresh-Debian case. Verified against a stripped PATH with no tmux, cargo, git or package manager.inputgroup which covers every input device including the keyboard.Installer
Preflight checks everything — distro, package manager, display manager, WM, terminal, tmux, cargo, git, screen locker, touch device, permissions, free disk — and prints the total cost for one confirmation before touching anything. Prompts read
/dev/ttyso they survivecurl | sh.Source-only for now; no prebuilt binaries.
[package.metadata.deb]is in place for later.Measured, not guessed
On the LattePanda (Atom x5-Z8350, 4 cores, 1.9 GB, no swap, toolchain present): build 108 s, peak 1.1 GB, 103 MB target dir, 946 KB binary. The README had said twenty minutes and the installer had budgeted 600 MB; both were guesses and both are corrected. Also confirms tmux 3.1c works, which was untested.
Idle cost on the display host: 234 MB used of 1.9 GB with all eight panes up, and 0.0% CPU for the gesture process — it blocks on the device rather than polling.
Testing
43 tests: the grid model, the gesture classifier, protocol-B decoding, and three end-to-end passes driving a real tmux server.
cargo fmtandcargo clippy -D warningsclean, no#[allow].Every README example verified verbatim against the binary; every relative link resolves.
Docs
README rewritten for a first-time reader. Mechanism and reasoning moved to
notes/(DESIGN.md,HARDWARE-NOTES.md,V1-BASH.md,TODO.md), including the two operational traps the deployment turned up:i3-msg exitstrands a keyboard-less display because lightdm autologin fires at seat start, andssh host 'sudo ...'has no TTY to prompt on.cad/README.mdwas a byte-identical copy of the one insidegeeekpi_rack_adapter_release_v1/, so every path in it including the screenshot was broken from where it sits — corrected, and it now states once that the 9-inch screen, 10-inch mini-rack mount and 19-inch rack are three separate measurements.Migration
v1 is removed here but recoverable at tag
v1.2;notes/V1-BASH.mdhas a setting-by-setting migration table. There is deliberately no converter.After merging
/usr/local/binthat need root to remove — seenotes/TODO.md.🤖 Generated with Claude Code