# Design notes Why the thing is built the way it is. The README says what to do; this says why, so neither has to carry both jobs. ## The carousel is tmux pane zoom, not extra processes There is exactly one monitor process per host. Zooming calls `tmux resize-pane -Z`, which makes one pane fill the window and sends `SIGWINCH` so the program repaints at the new size. Three reasons that matters: - **Half the processes.** Separate full-screen instances would mean N extra processes and double the polling load on the monitored hosts. On a 1.9 GB display host that is the difference between comfortable and not. - **No reconnect delay.** Hidden panes keep running and stay connected, so swiping back shows current data, not a stale snapshot with a spinner. - **Constant load.** The monitored hosts see the same connections regardless of what is on screen. ## Why the grid is sparse ordinals The obvious design is that a coordinate names a physical slot. It falls apart on the first socktop group: a group of four hosts is five screens, so putting anything to its right means writing `0x5`, and adding a fifth host means renumbering the rest of the row. Making coordinates pure ordering removes that entirely. Only the sort order matters, so `0x1` and `0x5` are the same thing, and a group grows without disturbing its neighbours. The cost is that the file is not a literal map of the screen — which is what `socktop-swipe validate` is for. ## Why return memory beats spatial snapping Vertical movement had two plausible rules and they disagree. Purely spatial: from `0x1`, up to `-1x0`, down again lands on `0x0`. Return memory: it lands back on `0x1`. Return memory won because the thing you actually do with a wall display is glance away and glance back. Losing your place on every glance is the worse failure, and snapping is only ever needed to decide the *first* entry into a row. A layout where every row has a cell in the same column never snaps at all. ## Why evdev instead of lisgd libinput deliberately emits gesture events only for touchpads, never for touchscreens, so `libinput-gestures` and everything built on it cannot work here at all. Something has to read raw touch events. v1 used [lisgd](https://git.sr.ht/~mil/lisgd); v2 does it in-process. What that bought: - **No C toolchain in the install path.** No `libinput-dev`, no `libX11-dev`, no `git clone` and `make`. On the Wyse's 8 GB of eMMC that is not a small thing. - **`grab: true` replaces the X ignore rule.** `EVIOCGRAB` takes the device exclusively, so X never sees the touches — which is what v1's `Option "Ignore"` InputClass was faking, except this needs no X restart and no logout. The X rule is still documented in the README as a fallback for anyone who wants touch to reach other applications. - **One process, so the double-instance bug cannot happen.** lisgd does not grab the device, so two copies made every swipe fire twice and the carousel appeared to skip. Now the second copy fails to grab and says so. - **The contact-count workaround became honest.** Instead of binding three separate lisgd gestures per direction, the peak contact count is a field on the detected swipe, and `doctor` prints it in English. Averaging rather than summing the contacts' travel matters here: a panel reporting one physical finger as three contacts must not look like three times the displacement. There is a test for exactly that. ## Why panes are addressed by id, and kept alive Pane *indices* renumber when a pane dies. Pane *ids* (`%12`) do not, so every lookup uses them. That leaves the question of what happens when a monitor exits — a typo in a `generic` command, a socktop that cannot reach its agent. tmux destroys a window when its last pane goes, which during construction breaks the next `split-window` with a baffling "no current target", and afterwards silently reshuffles the display. v1 used `remain-on-exit`, which cannot actually do the job: it is a **per-window** option that new windows do not inherit, so there is always a gap between creating a window and setting it. v2 wraps each command instead: ```sh ; s=$?; printf '\n[%s exited: status %s]\n' "$s"; while :; do sleep 86400; done ``` The pane outlives the command, and the failure is visible *on the wall display* with its exit status — which is what a wall display is for. tmux already runs each command under `sh`, so this costs one shell that stays resident per pane rather than one that execs away. ## Why `at: "0x0"` must be quoted YAML reads an unquoted `0x0` as the hexadecimal number 0. The nasty part is that `1x0` is *not* valid hex and arrives as a string, so only the row-0 entries break and the failure looks arbitrary. Deserialization catches the integer case and prints the fix rather than a type error. ## Multiplexer: tmux, with zellij shelved `src/session/` is a `Multiplexer` trait with a tmux implementation behind it, so the question is cheap to reopen. It was shelved rather than rejected, for reasons worth recording so it is not re-litigated: 1. **"Available as a crate" is not an embedding API.** `zellij-server`, `zellij-client` and `zellij-utils` are published, but they are workspace crates for the binary, not a supported library surface. Realistic integration is the CLI or a WASM plugin — so it would still be a subprocess driven over a CLI, exactly like tmux. 2. **Addressability is what we depend on.** The grid needs *"focus cell 0x1, sub-screen 3, zoomed"* as one deterministic call. tmux gives that directly (`select-pane -t %12`, `resize-pane -Z`). zellij's CLI is direction-oriented (`move-focus left`), which would mean counting relative moves and tracking state we cannot verify. 3. **Footprint runs the wrong way.** zellij is a client/server async multiplexer with a wasmtime plugin runtime, heavier at idle than tmux's C implementation. On 2 GB boxes that is the binding constraint, and `apt install tmux` versus a zellij source build on an Atom is a much worse story for the install guide. The one idea worth keeping on the shelf: running the navigation state machine as a **WASM plugin inside zellij** via `zellij-tile`, which would give real event subscriptions instead of driving a CLI. Revisit only if tmux becomes the bottleneck.