351 lines
14 KiB
Markdown
351 lines
14 KiB
Markdown
# socktop-swipe
|
|
|
|
A rack-mounted touchscreen swipable display that monitors your servers via socktop and other status tuis.
|
|
|
|
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 | Status |
|
|
| --- | --- | --- |
|
|
| Display host | LattePanda (Atom x5-Z8350, 1.9 GB), Debian 11, i3 on X11 | in daily use |
|
|
| Display host | Dell Wyse 3040 (Atom x5-Z8350, 2 GB), 8/16 GB eMMC, DP out | not yet tried |
|
|
| Panel | GeeekPi 9" 1280x720 3U touchscreen, ILITEK USB touch | in daily use |
|
|
| Mounting | [`cad/`](cad) — 19" rack adapter | printed and in the rack |
|
|
|
|
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.
|