9226224507
Two bugs found deploying to the LattePanda, both of which v1 also had. Window options do not propagate from the session and new windows do not inherit them. `set-option -t <session> pane-border-status` quietly applies to whichever window happens to be current, so only ONE window ever got pane borders -- v1's rack display has had unlabeled borders on two of its three windows this whole time and nobody noticed, because the window that got them was the one usually on screen. This is the same trap that makes remain-on-exit useless here. pane-border-status, pane-border-format and allow-rename are now set per window in place(), and a test asserts every window has them. Pane labels no longer use `select-pane -T`. The pane *title* is writable by whatever runs in the pane: unifly probes for Kitty graphics support on startup and tmux consumed part of that probe as a title change, so the border read "Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA" instead of "unifly". Labels now live in a pane-scoped user option, @socktop_label, which no escape sequence can reach; pane-border-format falls back to the title if it is somehow unset. Also: `terminal:` now expands a leading ~/ like `binaries:` already did. A window manager's PATH rarely includes ~/.cargo/bin, so a full path is the usual answer there and should not have to be spelled out longhand. Build cost corrected from guesses to measurements on the LattePanda (Atom x5-Z8350, 4 cores, 1.9 GB, no swap, toolchain already present): 108 seconds, peak 1.1 GB, 103 MB target directory, 946 KB binary. The README said twenty minutes and the installer budgeted 600 MB; both were wrong. Integration tests now use one tmux session name each -- cargo runs them in parallel and they were tearing down each other's server state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
351 lines
14 KiB
Markdown
351 lines
14 KiB
Markdown
# socktop-swipe
|
|
|
|
A rack-mounted touchscreen you swipe through to see what your machines are doing.
|
|
|
|
Terminal dashboards are arranged in a grid. Swipe left and right to move along a
|
|
row; swipe up and down to move between rows. A group of hosts is one place in
|
|
that grid: it shows them tiled together first, then each one full-screen as you
|
|
keep swiping.
|
|
|
|

|
|
|
|
<video controls src="media/swipe-demo.mp4"></video>
|
|
|
|
Built for a 9" panel bolted into a 19" server rack, driven by a low-power x86 box
|
|
running i3 on X11.
|
|
|
|
## What you're looking at
|
|
|
|
The photo is a 9" touchscreen in a 19" rack, showing four Raspberry Pis at once
|
|
via [socktop](https://github.com/jasonwitty/socktop). It is a wall display: no
|
|
keyboard, no mouse, nothing to log into. It comes back by itself after a power
|
|
cut and it never blanks.
|
|
|
|
Everything on it is a terminal program. socktop-swipe does not draw anything
|
|
itself — it decides what is on screen and reads your finger.
|
|
|
|
```
|
|
unifly -1x0
|
|
(UniFi network)
|
|
↕
|
|
┌──────────────────────────────┐ ┌──────────────────┐
|
|
│ 4 Raspberry Pis │→ │ 2 more machines │ row 0
|
|
│ tiled, then each full-screen│ │ │
|
|
└──────────────────────────────┘ └──────────────────┘
|
|
0x0 0x1
|
|
↕
|
|
uptime kuma 1x0
|
|
(service status)
|
|
```
|
|
|
|
Swipe forward through row 0 and you get: all four Pis tiled, then `rpi-master`
|
|
full-screen, then each of the others, then on to the next group. Swipe up at any
|
|
point for the UniFi network view; swipe back down and you are on the exact host
|
|
you left.
|
|
|
|
## Hardware
|
|
|
|
| Part | What was used | Notes |
|
|
| --- | --- | --- |
|
|
| Display host | LattePanda (Atom x5-Z8350, 1.9 GB) | Debian 11, i3 on X11 |
|
|
| Display host | Dell Wyse 3040 (Atom x5-Z8350, 2 GB) | ~$35 used; 8/16 GB eMMC, DP out |
|
|
| Panel | GeeekPi 9" 1280x720 3U touchscreen | ILITEK USB touch |
|
|
| Mounting | [`cad/`](cad) — 19" rack adapter | 3D-print or laser-cut aluminium |
|
|
|
|
Any Linux box with a USB or DSI touchscreen will do. It is deliberately modest
|
|
hardware: the whole point is a display that idles at a few watts.
|
|
|
|
The panel is sold as a **10-inch mini-rack** part. [`cad/`](cad) has a pair of
|
|
side adapters — OpenSCAD source, STLs with tested print settings, and
|
|
SendCutSend-ready DXFs — that mount it in a standard **19-inch** rack, with three
|
|
keystone jack positions on each side. The geometry has been physically tested.
|
|
|
|
## Install
|
|
|
|
```sh
|
|
curl -fsSL https://gt.wittyoneoff.com/jason/socktop-swipe/raw/branch/main/install.sh | sh
|
|
```
|
|
|
|
It checks everything first and prints the whole cost — packages, toolchain, disk
|
|
— before changing anything. Then it asks, one thing at a time:
|
|
|
|
- which touch device to use (it looks for one and offers it)
|
|
- how to grant access to it: a udev rule for touchscreens only, or the `input`
|
|
group
|
|
- whether to install socktop, uptime-kuma-status or unifly
|
|
- whether to stop the screen blanking, sleeping and locking
|
|
- whether to autostart on login
|
|
- whether to log straight in at boot, so it comes back after a power cut
|
|
|
|
Every answer has a sensible default and nothing is required. Piping into `sh`
|
|
leaves no keyboard on standard input, so the script reads your terminal directly;
|
|
if there is no terminal at all it takes the defaults and says so. `--yes` skips
|
|
the questions.
|
|
|
|
There are no prebuilt binaries yet — it builds from source, so a Rust toolchain
|
|
is installed if you do not have one.
|
|
|
|
Measured on the LattePanda (Atom x5-Z8350, 4 cores, 1.9 GB RAM, no swap), with a
|
|
toolchain already present: **108 seconds**, peaking at 1.1 GB of the 1.9 GB and
|
|
leaving a 103 MB build directory. The 946 KB binary is the only thing installed.
|
|
Add roughly 1.2 GB and a few minutes if rustup has to be fetched too. The
|
|
installer checks free space first and tells you if it will not fit.
|
|
|
|
<details>
|
|
<summary>From a checkout instead</summary>
|
|
|
|
```sh
|
|
git clone https://gt.wittyoneoff.com/jason/socktop-swipe
|
|
cd socktop-swipe
|
|
./install.sh
|
|
```
|
|
</details>
|
|
|
|
## Your first config
|
|
|
|
The config lives at `~/.config/socktop-swipe/config.yaml`. The installer writes a
|
|
starting point; this section builds one up from nothing.
|
|
|
|
**Check every edit** with:
|
|
|
|
```sh
|
|
socktop-swipe validate
|
|
```
|
|
|
|
which resolves the grid, prints the map, and tells you if a program it would run
|
|
is not actually there.
|
|
|
|
### One group of hosts
|
|
|
|
```yaml
|
|
touch:
|
|
device: /dev/input/by-id/usb-ILITEK_ILITEK-TOUCH-event-if00
|
|
width: 1280
|
|
height: 720
|
|
|
|
screens:
|
|
- at: "0x0"
|
|
type: socktop
|
|
socktop_group: [rpi-master, rpi-worker-1, rpi-worker-2, rpi-worker-3]
|
|
```
|
|
|
|
That is five screens: the four hosts tiled, then each one full-screen.
|
|
|
|
```
|
|
0x0 tiled → rpi-master → rpi-worker-1 → rpi-worker-2 → rpi-worker-3
|
|
```
|
|
|
|
> **Quote the coordinate.** Unquoted, YAML reads `0x0` as the hexadecimal number
|
|
> zero. `socktop-swipe validate` will tell you so if you forget.
|
|
|
|
### Something above it
|
|
|
|
```yaml
|
|
- at: "-1x0"
|
|
type: unifly
|
|
```
|
|
|
|
Row `-1` is above row `0`. Now swiping up from any of those five screens shows
|
|
the UniFi TUI, and swiping down returns you to the exact host you were on.
|
|
|
|
### Something below, and more hosts alongside
|
|
|
|
```yaml
|
|
- at: "0x1"
|
|
type: socktop
|
|
socktop_group: "orangepi, trixie"
|
|
layout: even-vertical
|
|
|
|
- at: "1x0"
|
|
type: uptime-kuma-status
|
|
url: https://status.example.com/status/mine
|
|
```
|
|
|
|
`0x1` sits to the right of `0x0`, so you reach it by swiping forward past the
|
|
last Pi. `1x0` is below.
|
|
|
|
Full worked example with every option commented:
|
|
[`config.example.yaml`](config.example.yaml).
|
|
|
|
## How the grid works
|
|
|
|
**Row increases downward.** `-1x0` is above `0x0`; `1x0` is below it. Column
|
|
increases rightward. You start at `0x0`.
|
|
|
|
**Coordinates are ordering, not slots.** Only their sort order matters, so `0x1`
|
|
and `0x5` are interchangeable. You never have to count how many screens a group
|
|
produces in order to place something next to it — add a host to a group and
|
|
nothing else needs renumbering.
|
|
|
|
**A group of hosts is one place in the grid.** Swiping forward walks its screens
|
|
— tiled overview, then each host — and only moves on to the next place after the
|
|
last one. Coming back from the right lands you on that group's *last* host, not
|
|
its overview, so the row reads as one continuous strip.
|
|
|
|
**Swiping up or down returns you to where you were.** Each row remembers its
|
|
position. If you glance at the network view and come back, you are on the same
|
|
host, still zoomed.
|
|
|
|
**Nothing wraps around.** Swiping past either end of a row does nothing. On a
|
|
wall display, wrapping makes it impossible to tell where you are.
|
|
|
|
If a row has no screen in your current column, you land on the nearest one —
|
|
`0x3` swiping up with only `-1x0` above goes there. That only happens the first
|
|
time; after that the row remembers.
|
|
|
|
## Monitor types
|
|
|
|
Every type also accepts `command:` to replace the generated command outright, and
|
|
`title:` to change the label on the pane border.
|
|
|
|
| `type:` | Parameters | What it runs |
|
|
| --- | --- | --- |
|
|
| `socktop` | `socktop_group` (list, or comma-separated string), `layout` | one [socktop](https://github.com/jasonwitty/socktop) per host |
|
|
| `uptime-kuma-status` | `url` | [uptime-kuma-status](https://github.com/jasonwitty/uptime-kuma-status) against a public [Uptime Kuma](https://github.com/louislam/uptime-kuma) status page |
|
|
| `unifly` | *(none yet)* | [unifly](https://github.com/jasonwitty/unifly) `tui` |
|
|
| `generic` | `command`, `title` | anything you like |
|
|
|
|
`layout` takes any tmux layout name: `tiled` (the default), `even-horizontal`,
|
|
`even-vertical`, `main-horizontal`, `main-vertical`.
|
|
|
|
```yaml
|
|
- at: "1x1"
|
|
type: generic
|
|
title: k3s
|
|
command: journalctl -f -u k3s-agent
|
|
```
|
|
|
|
## Configuration reference
|
|
|
|
| Key | Default | What it does |
|
|
| --- | --- | --- |
|
|
| `session` | `socktop-swipe` | tmux session name |
|
|
| `terminal` | *(none)* | Terminal that `run` opens the dashboard in. Unset attaches in the current one |
|
|
| `indicator` | `false` | Show position — what is above, where you are, what is below — in the status line |
|
|
| `binaries.socktop` etc. | looked up on `PATH` | Full path to each program. `~/` is expanded |
|
|
| `touch.device` | — | **Always a `/dev/input/by-id/` path**; `eventN` numbers change on reboot |
|
|
| `touch.width` / `.height` | — | The **panel's** resolution, not the X screen |
|
|
| `touch.grab` | `true` | Take the panel exclusively so X never sees the touches |
|
|
| `touch.threshold` | `80` | Pixels of travel before a drag counts as a swipe |
|
|
| `touch.leniency` | `30` | Degrees off-axis tolerated, max 45 |
|
|
| `touch.fingers` | `[1, 2, 3]` | Contact counts accepted. **The setting most likely to need changing** |
|
|
| `gestures.forward` / `.back` | `RL` / `LR` | Named by finger motion. Swap to reverse |
|
|
| `gestures.up` / `.down` | `DU` / `UD` | |
|
|
|
|
### Commands
|
|
|
|
| Command | |
|
|
| --- | --- |
|
|
| `socktop-swipe run` | Build the session, open it in a terminal, read the panel. The one thing to autostart |
|
|
| `socktop-swipe validate` | Check the config and print the grid map |
|
|
| `socktop-swipe doctor` | Swipe, and be told what the panel reported |
|
|
| `socktop-swipe doctor --list` | List touch devices — works before any config exists |
|
|
| `socktop-swipe forward` / `back` / `up` / `down` | Move a running instance. For keybindings, or a box with no panel |
|
|
| `socktop-swipe build` / `attach` / `daemon` | The pieces of `run`, separately, for a systemd split |
|
|
|
|
## Unattended operation
|
|
|
|
A wall display has to survive a power cut with nobody in the room.
|
|
|
|
**Never blank.** The installer writes an Xorg `ServerFlags` snippet with all four
|
|
timeouts at zero, which covers every session including the display manager's
|
|
greeter and survives reboots, *and* adds an `xset` line to the session autostart,
|
|
because a session can re-enable blanking after X starts. Both are needed. Check
|
|
with `xset q | grep -A1 -E 'Screen Saver|DPMS'` — you want `timeout: 0`,
|
|
`prefer blanking: no`, `DPMS is Disabled`.
|
|
|
|
**Come back by itself.** Without autologin, a reboot leaves the panel at a login
|
|
prompt and nothing starts. The installer offers a lightdm drop-in. It is opt-in
|
|
and defaults to no, because **anyone with physical access to the panel gets that
|
|
user's session** — only do it where the account is nothing but the dashboard.
|
|
Delete `/etc/lightdm/lightdm.conf.d/50-autologin-socktop-swipe.conf` to undo it.
|
|
|
|
**Autostart.** `socktop-swipe run` is a single process that builds the session,
|
|
opens the terminal and reads the panel, so one line in your window manager's
|
|
autostart is enough. For a systemd user session,
|
|
[`packaging/socktop-swipe.service`](packaging/socktop-swipe.service) splits it up.
|
|
|
|
## Troubleshooting
|
|
|
|
Start here:
|
|
|
|
```sh
|
|
socktop-swipe doctor
|
|
```
|
|
|
|
It watches the panel and says what it saw in English — direction, contact count,
|
|
and why a gesture was ignored.
|
|
|
|
### Swipes are detected but nothing happens
|
|
|
|
`doctor` will say `saw 2 contact(s), config accepts [1]`. Many multipoint panels
|
|
report two or even three contacts for a physically one-finger swipe. Add them to
|
|
`touch.fingers`. This is the single most common problem on new hardware.
|
|
|
|
### Nothing is detected at all
|
|
|
|
In order: is the device path right (`socktop-swipe doctor --list`)? Can you read
|
|
it — is the udev rule installed, or are you in the `input` group and did you log
|
|
out and back in? Is the panel plugged in?
|
|
|
|
### Swipes work, but the terminal also reacts — text zooming, panes resizing
|
|
|
|
X is delivering touch to whatever is on screen as well. `touch.grab: true` (the
|
|
default) prevents this by taking the device exclusively. If you have set it to
|
|
`false` because you want touch in other applications, tell X to ignore the panel
|
|
instead:
|
|
|
|
```sh
|
|
sudo tee /etc/X11/xorg.conf.d/99-ignore-touch-socktop-swipe.conf >/dev/null <<'EOF'
|
|
Section "InputClass"
|
|
Identifier "ignore touchscreen (socktop-swipe)"
|
|
MatchProduct "ILITEK"
|
|
MatchIsTouchscreen "on"
|
|
Option "Ignore" "on"
|
|
EndSection
|
|
EOF
|
|
```
|
|
|
|
Adjust `MatchProduct` to your panel, and restart X.
|
|
|
|
### A pane shows `[... exited: status 1]`
|
|
|
|
That monitor program stopped. The pane is deliberately kept so the layout does
|
|
not reshuffle and you can see what happened. Check the path under `binaries:`,
|
|
and that the program works when you run it by hand.
|
|
|
|
### The dashboard is on a screen that isn't there
|
|
|
|
Some boards report an output with no panel behind it, and X will happily put the
|
|
dashboard on it — everything looks fine over ssh while the real panel shows an
|
|
empty desktop. See [`notes/HARDWARE-NOTES.md`](notes/HARDWARE-NOTES.md).
|
|
|
|
## Uninstall
|
|
|
|
```sh
|
|
./uninstall.sh # binary, config, udev rule, X snippets, autologin
|
|
./uninstall.sh --keep-config # keep your settings
|
|
```
|
|
|
|
The Rust toolchain, tmux, the monitor programs and your `input` group membership
|
|
are left alone; the script prints how to remove each.
|
|
|
|
## Notes
|
|
|
|
Design decisions and the reasoning behind them are in [`notes/`](notes):
|
|
|
|
- [`DESIGN.md`](notes/DESIGN.md) — why tmux, why evdev, why the grid works this way
|
|
- [`HARDWARE-NOTES.md`](notes/HARDWARE-NOTES.md) — panel quirks, phantom outputs, the `sudo xset` trap
|
|
- [`V1-BASH.md`](notes/V1-BASH.md) — the shell implementation this replaced, and how to migrate
|
|
- [`TODO.md`](notes/TODO.md)
|
|
|
|
## Related
|
|
|
|
- [socktop](https://github.com/jasonwitty/socktop) — the host monitor this was built around
|
|
- [uptime-kuma-status](https://github.com/jasonwitty/uptime-kuma-status) — Uptime Kuma status pages in a terminal
|
|
- [unifly](https://github.com/jasonwitty/unifly) — UniFi network TUI
|
|
|
|
## License
|
|
|
|
Apache-2.0.
|