socktop-swipe

Swipe-to-zoom touchscreen navigation for a wall-mounted socktop dashboard.

Hosts are arranged in groups. Each group is one tmux window showing its hosts tiled together. Swiping right-to-left on the panel zooms into each host full-screen, one at a time, then moves on to the next group's overview; swiping left-to-right walks back the same way.

   group A    ->   A1   ->   A2   ->   A3   ->   A4   ->   group B   ->   B1   ->   B2
   (quarters)    zoomed   zoomed   zoomed   zoomed      (stacked)     zoomed   zoomed
       <------------------------- swipe left-to-right -------------------------

With a single group this is simply overview -> each host -> back.

Optionally, swiping down from any screen shows a different TUI (an Uptime Kuma status page on the rack display), and swiping up returns to exactly the screen you left.

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 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

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 (gesture daemon + fullscreen terminal), with a backup and i3 -C validation
--noblank Also stop the screen blanking / DPMS power-down (wall displays)
--autologin Also make lightdm log this user straight into i3 at boot (see security note)

Re-running is safe: an existing config file is never overwritten, and the i3 edit is skipped if already present.

Verify

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:

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_GROUPS Pis quartered ; Orange Pi + Debian stacked Groups separated by ;, each a list of socktop profile names in swipe order, plus an optional @layout (tiled default, even-vertical stacks, even-horizontal side-by-side, main-* also work). Any count of groups and hosts works.
SOCKTOP_HOSTS empty Legacy single-group form; used only when SOCKTOP_GROUPS is empty
SOCKTOP_AUX_CMD uptime-kuma-status … Command for the swipe-down "aux" screen. Full path. Empty disables the feature and its gestures.
GESTURE_AUX_IN / GESTURE_AUX_OUT UD / DU Swipe down shows aux, swipe up returns
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_GROUPS and run socktop-rack. Nothing else needs touching — the carousel derives its length from the windows and panes at runtime. Every host named must exist in ~/.config/socktop/profiles.json on the display host.

# four Pis as quarters, then two more boxes stacked one above the other
SOCKTOP_GROUPS="rpi-master rpi-worker-1 rpi-worker-2 rpi-worker-3 ; orangepi trixie @even-vertical"

Autostart

./install.sh --i3 appends to ~/.config/i3/config. For systemd user sessions instead:

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:

xset q | grep -A1 -E 'Screen Saver|DPMS'

You want timeout: 0, prefer blanking: no, and DPMS is Disabled.

Some boards report a display output that has no panel behind it. The LattePanda's onboard DSI header shows up as DSI-1 connected 1024x600 with zero EDID bytes (xrandr prints 0mm x 0mm), and X happily puts workspace 1 on it. Everything then looks healthy from ssh -- i3 has the window fullscreen on its primary output -- while the real panel shows an empty workspace. Check with:

xrandr | grep ' connected'          # a real panel has physical mm, phantoms say 0mm x 0mm
for c in /sys/class/drm/card0-*; do echo "$c $(cat $c/status) edid=$(wc -c <$c/edid)"; done

Tell X to ignore the phantom and mark the real panel primary (adjust the names):

sudo tee /etc/X11/xorg.conf.d/20-outputs-socktop-swipe.conf >/dev/null <<'EOF'
Section "Monitor"
    Identifier "DSI-1"
    Option     "Ignore" "true"
EndSection
Section "Monitor"
    Identifier "HDMI-2"
    Option     "Primary" "true"
EndSection
EOF

Then set SCREEN_W / SCREEN_H in the config to the real panel's mode.

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:

grep -E '^autologin-(user|session)' /etc/lightdm/lightdm.conf

No output means no autologin. ./install.sh --autologin writes /etc/lightdm/lightdm.conf.d/50-autologin-socktop-swipe.conf for the current user into the i3 session. It is opt-in, not a default: anyone with physical access to the panel gets that user's session, so only do it where the display user is nothing but the dashboard. Delete the file to get the login prompt back.

Uninstall

./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.

S
Description
Swipe-to-zoom touchscreen navigation for a tiled socktop dashboard (tmux pane zoom + lisgd)
Readme 18 MiB
Languages
G-code 96.6%
Rust 2.9%
Shell 0.5%