# 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. ![The touch panel mounted in the rack, showing four Raspberry Pis tiled together](media/rack-display.jpg) 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. Budget about 1.8 GB and, on an Atom, twenty minutes or so.
From a checkout instead ```sh git clone https://gt.wittyoneoff.com/jason/socktop-swipe cd socktop-swipe ./install.sh ```
## 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.