# socktop-swipe Swipe-to-zoom touchscreen navigation for a wall-mounted [socktop](https://crates.io/crates/socktop) dashboard. One tmux window shows every monitored host tiled together. Swiping right-to-left on the panel zooms into each host full-screen, one at a time; swiping left-to-right walks back out to the overview. ``` overview -> host 1 -> host 2 -> host 3 -> host 4 (tiled) zoomed zoomed zoomed zoomed <-------------- swipe left-to-right -------------- ``` Built for a 10" touch panel on a server rack driving four Raspberry Pis, on a low-power x86 box running i3 on X11. ## How it works, and why **The carousel is tmux pane zoom, not extra socktop instances.** There is exactly one socktop process per host. Zooming calls `tmux resize-pane -Z`, which makes one pane fill the window and sends `SIGWINCH` so socktop repaints at the new size. That matters for three reasons: - **Half the processes.** Separate full-screen instances would mean N extra socktop 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 to a host shows current data, not a stale snapshot. - **Constant load.** The monitored hosts see the same connections regardless of what is on screen. **Gestures come from lisgd, below the terminal.** No Linux terminal maps horizontal swipes to commands, so this cannot be done in terminal config. Critically, **libinput deliberately emits gesture events only for touchpads, never for touchscreens** — so `libinput-gestures` and similar tools cannot work here at all. [lisgd](https://git.sr.ht/~mil/lisgd) reads raw touch events and synthesises the swipes itself, reading `/dev/input` directly and bypassing X. ## Requirements - `tmux`, `socktop`, and a working `~/.config/socktop/profiles.json` - `lisgd` (the installer builds it) - A C toolchain plus `libinput` and `libX11` headers, to build lisgd - Membership of the `input` group (the installer adds you; needs a relogin) ## Install ```sh git clone https://gt.wittyoneoff.com/jason/socktop-swipe cd socktop-swipe ./install.sh --xignore --i3 ``` Then **log out and back in** — the `input` group and the X rule both need a fresh session. | Flag | Effect | | --- | --- | | *(none)* | Build/install lisgd, install the three scripts and the default config | | `--xignore` | Also tell X to ignore the touch panel (see below — usually wanted) | | `--i3` | Also add i3 autostart lines, with a backup and `i3 -C` validation | | `--noblank` | Also stop the screen blanking / DPMS power-down (wall displays) | Re-running is safe: an existing config file is never overwritten, and the i3 edit is skipped if already present. ### Verify ```sh tools/find-device.sh # confirm TOUCH_DEV is right tools/diag.sh # stage 1: device live? stage 2: what does lisgd see? tools/test-foreground.sh # run the real daemon, watch the display, swipe ``` ## Configuration Everything lives in **`/usr/local/etc/socktop-swipe.env`**. Edit it there, not in the repo copy. After changing it, restart the daemon and rebuild the session: ```sh socktop-gestures --replace & # restarts the daemon, running or not socktop-rack ``` Use `--replace` rather than `pkill -x lisgd && socktop-gestures`: `pkill` exits non-zero when it matches nothing, so that one-liner silently starts **nothing** if no daemon was running — you would swipe, get no response, and reasonably blame the config change. | Option | Default | What it does | | --- | --- | --- | | `SOCKTOP_HOSTS` | four Pi profiles | socktop profile names, **in swipe order**. Any count works; layout and carousel adapt. | | `SOCKTOP_SESSION` | `socktop4` | tmux session name | | `SOCKTOP_BIN` | `$HOME/.cargo/bin/socktop` | Full path — i3 does not have `~/.cargo/bin` on `PATH` | | `TOUCH_DEV` | ILITEK by-id path | Touch device. **Always use a `/dev/input/by-id/` path**; `eventN` numbers change on reboot. | | `SCREEN_W` / `SCREEN_H` | `1024` / `600` | The **panel's** resolution, not the X screen. See gotcha 2. | | `SWIPE_THRESHOLD` | `80` | Pixels of travel before a drag counts as a swipe | | `SWIPE_LENIENCY` | `30` | Degrees off-axis tolerated (max 45) | | `FINGER_COUNTS` | `1 2 3` | Contact counts accepted. **The setting most likely to need changing** — see gotcha 1. | | `GESTURE_IN` / `GESTURE_OUT` | `RL` / `LR` | Swap these to reverse swipe direction | ### Adding or removing hosts Edit `SOCKTOP_HOSTS` and run `socktop-rack`. Nothing else needs touching — the carousel derives its length from the pane count at runtime. ## Autostart `./install.sh --i3` appends to `~/.config/i3/config`. For systemd user sessions instead: ```sh cp socktop-swipe.service ~/.config/systemd/user/ systemctl --user enable --now socktop-swipe ``` Either way, **run exactly one gesture daemon**. lisgd does not grab the input device exclusively, so two instances make every swipe fire twice. `socktop-gestures` enforces this: it exits non-zero if one is already running, unless given `--replace`. ## Stopping the screen blanking A wall display must never blank. `./install.sh --noblank` applies two layers: 1. An Xorg `ServerFlags` snippet with all four timeouts at `0`, so it covers every X session including the display-manager greeter, and survives reboots. 2. An `xset s off s noblank -dpms` line in the i3 autostart, because a session or DM can re-enable the screensaver after X has started. Both are needed. `xset` alone does not survive a reboot, and — the trap worth knowing — **`xset` must run as the session user**. Running `sudo xset ...`, or a `sudo bash noblank.sh` wrapper, targets root's X connection and silently does nothing while appearing to succeed. Check the real state with: ```sh xset q | grep -A1 -E 'Screen Saver|DPMS' ``` You want `timeout: 0`, `prefer blanking: no`, and `DPMS is Disabled`. ### Related: does the display come back by itself? Blanking is only half of unattended operation. If the display manager has no autologin configured, a reboot leaves the panel at a login prompt and nothing autostarts. Check with: ```sh grep -E '^autologin-(user|session)' /etc/lightdm/lightdm.conf ``` No output means no autologin. Configuring it is out of scope here — it weakens the machine's security posture and is a deliberate choice, not a default. ## Uninstall ```sh ./uninstall.sh # scripts, config, X rule ./uninstall.sh --keep-config # keep your settings ``` lisgd, the `input` group and your socktop profiles are deliberately left alone; the script prints how to remove each. ## Troubleshooting Four gotchas account for nearly every failure. All were found the hard way. ### 1. Gestures are recognised but never fire Run `tools/diag.sh` and read stage 2: ``` [swipe]: Cfg(f=1/s=3/e=0/d=0) <=> Evt(f=2/s=3/e=1/d=2) ^ configured ^ what happened ``` Matching `s=` with differing `f=` means **your panel reports more contacts than you configured**. Many multipoint panels report 2 or even 3 contacts for a physically one-finger swipe. Add them to `FINGER_COUNTS`. Direction enum: `0=DU`, `1=UD`, `2=LR`, `3=RL`. ### 2. Swipes register, but erratically — resizing panes, zooming text X is also delivering touch to whatever is on screen, so a single swipe hits several consumers at once: tmux mouse mode drags pane borders, the terminal reads 2-contact swipes as pinch-zoom, and socktop enables its own all-motion mouse reporting (`?1003h`). Meanwhile lisgd fires too. Fix with `./install.sh --xignore` and relog. lisgd is unaffected because it reads the device directly. Trade-off: no tap or pinch-zoom on that panel — terminal keyboard zoom (`Ctrl +` / `Ctrl -`) still works. Also ensure tmux mouse mode is **off**; `socktop-rack` sets this. ### 3. Every swipe jumps two panes Two lisgd instances are running. `pgrep -x lisgd` should show exactly one. `socktop-gestures` refuses to start if a daemon is already running, so this should not happen via the normal path; `--replace` restarts cleanly. It can still occur if lisgd was launched by hand. ### 4. Swipes do nothing at all In order: is the session running (`tmux ls`)? Is the daemon running (`pgrep -x lisgd`)? Does `tools/diag.sh` stage 1 read bytes? Are you in the `input` group (`id -nG | grep input`, needs a relogin)? Note lisgd's verbose flag is **`-v`**, not `-D`. ### Multi-monitor note If the display host has a second monitor, X reports the **combined** root window (e.g. 2304x720). lisgd would scale its maths to that, so `SCREEN_W`/`SCREEN_H` must describe the touch panel alone. `socktop-gestures` always passes them explicitly. ## Known limitations - **~300 ms repaint on zoom-in.** Between tmux making the pane full-screen and socktop repainting, you briefly see the old small rendering anchored top-left. This is socktop's redraw latency, not the gesture path; nothing outside socktop can fix it. Zooming out has no visible artifact, since panes return to sizes they were already drawn at. - Deliberately **no wrap-around** at the ends of the carousel — on a wall display wrapping makes it impossible to tell where you are. - X11 only. The lisgd build disables its Wayland backend (`WITHOUT_WAYLAND=1`). ## Tested on LattePanda (Atom x5-Z8350, 1.9 GB RAM, Cherry Trail graphics), 1024x600 ILITEK DSI touch panel, Debian 11, i3 on X11, Alacritty 0.16.1, lisgd from git, monitoring four Raspberry Pis.