Files

128 lines
6.2 KiB
Markdown
Raw Permalink Normal View History

# 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
<command>; s=$?; printf '\n[%s exited: status %s]\n' <name> "$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.