Add host groups (one tmux window per group) and lightdm --autologin
SOCKTOP_GROUPS splits hosts into ';'-separated groups, each its own tmux window with an optional @layout. The carousel walks group overview -> each pane zoomed -> next group overview. SOCKTOP_HOSTS still works as one group. install.sh --autologin writes a lightdm.conf.d drop-in; uninstall removes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,16 +3,19 @@
|
||||
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.
|
||||
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.
|
||||
|
||||
```
|
||||
overview -> host 1 -> host 2 -> host 3 -> host 4
|
||||
(tiled) zoomed zoomed zoomed zoomed
|
||||
<-------------- swipe left-to-right --------------
|
||||
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.
|
||||
|
||||
Built for a 10" touch panel on a server rack driving four Raspberry Pis, on a
|
||||
low-power x86 box running i3 on X11.
|
||||
|
||||
@@ -63,6 +66,7 @@ fresh session.
|
||||
| `--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) |
|
||||
| `--autologin` | Also make lightdm log this user straight into i3 at boot (see [security note](#related-does-the-display-come-back-by-itself)) |
|
||||
|
||||
Re-running is safe: an existing config file is never overwritten, and the i3
|
||||
edit is skipped if already present.
|
||||
@@ -92,7 +96,8 @@ 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_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_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. |
|
||||
@@ -104,8 +109,14 @@ blame the config change.
|
||||
|
||||
### 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.
|
||||
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.
|
||||
|
||||
```sh
|
||||
# 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
|
||||
|
||||
@@ -152,8 +163,11 @@ autostarts. Check with:
|
||||
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.
|
||||
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
|
||||
|
||||
|
||||
+18
-4
@@ -12,10 +12,24 @@
|
||||
# What to monitor
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# socktop profile names (from ~/.config/socktop/profiles.json), in swipe order.
|
||||
# The first is what you land on after one right-to-left swipe from the overview.
|
||||
# Any count works; the tiled layout and the carousel adapt automatically.
|
||||
SOCKTOP_HOSTS="${SOCKTOP_HOSTS:-rpi-master rpi-worker-1 rpi-worker-2 rpi-worker-3}"
|
||||
# What to show, as GROUPS separated by ";". Each group is one screen of the
|
||||
# carousel: a tiled overview of its hosts, then each host zoomed full-screen in
|
||||
# turn, then on to the next group. Host names are socktop profile names from
|
||||
# ~/.config/socktop/profiles.json.
|
||||
#
|
||||
# overview(A) -> A1 -> A2 -> ... -> overview(B) -> B1 -> B2 -> ...
|
||||
#
|
||||
# An optional "@layout" token in a group sets its tmux overview layout
|
||||
# (tiled, even-horizontal, even-vertical, main-horizontal, main-vertical).
|
||||
# Default is tiled: 4 hosts become quarters, 2 become side-by-side.
|
||||
# "@even-vertical" stacks the panes one above the other instead.
|
||||
#
|
||||
# The example below is the rack display: the four Pis quartered and zoomable,
|
||||
# then the Orange Pi and the Debian box stacked and zoomable.
|
||||
SOCKTOP_GROUPS="${SOCKTOP_GROUPS:-rpi-master rpi-worker-1 rpi-worker-2 rpi-worker-3 ; orangepi trixie @even-vertical}"
|
||||
|
||||
# Legacy single-group form. Used only when SOCKTOP_GROUPS is empty.
|
||||
SOCKTOP_HOSTS="${SOCKTOP_HOSTS:-}"
|
||||
|
||||
# tmux session name. Change only if it collides with something else.
|
||||
SOCKTOP_SESSION="${SOCKTOP_SESSION:-socktop4}"
|
||||
|
||||
+32
-1
@@ -5,7 +5,8 @@
|
||||
# ./install.sh --xignore ...and tell X to ignore the touch panel
|
||||
# ./install.sh --i3 ...and add i3 autostart lines
|
||||
# ./install.sh --noblank ...and stop the screen blanking
|
||||
# ./install.sh --xignore --i3 --noblank full wall-display setup
|
||||
# ./install.sh --autologin ...and make lightdm log this user straight into i3
|
||||
# ./install.sh --xignore --i3 --noblank --autologin full wall-display setup
|
||||
#
|
||||
# Safe to re-run: an existing config file is never overwritten, and the i3 edit
|
||||
# is skipped if already present.
|
||||
@@ -17,17 +18,20 @@ ETC="$PREFIX/etc"
|
||||
CONF="$ETC/socktop-swipe.env"
|
||||
XCONF=/etc/X11/xorg.conf.d/99-ignore-touch-socktop-swipe.conf
|
||||
BCONF=/etc/X11/xorg.conf.d/10-no-blanking-socktop-swipe.conf
|
||||
LCONF=/etc/lightdm/lightdm.conf.d/50-autologin-socktop-swipe.conf
|
||||
SRC="$HOME/src/lisgd"
|
||||
|
||||
here=$(cd "$(dirname "$0")" && pwd)
|
||||
do_xignore=no
|
||||
do_i3=no
|
||||
do_noblank=no
|
||||
do_autologin=no
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
--xignore) do_xignore=yes ;;
|
||||
--i3) do_i3=yes ;;
|
||||
--noblank) do_noblank=yes ;;
|
||||
--autologin) do_autologin=yes ;;
|
||||
*) echo "unknown option: $a" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
@@ -147,6 +151,33 @@ EOF
|
||||
relogin=yes
|
||||
fi
|
||||
|
||||
# --- optional: lightdm autologin --------------------------------------------
|
||||
if [ "$do_autologin" = yes ]; then
|
||||
# A wall display must come back by itself after a power cut. Without this a
|
||||
# reboot leaves the panel at the greeter with nothing autostarted.
|
||||
# SECURITY: anyone with physical access to the screen gets this user's
|
||||
# session. Only use it on a display whose user account is nothing but the
|
||||
# dashboard. Debian's lightdm-autologin PAM stack needs no extra group.
|
||||
if [ ! -d /etc/lightdm ]; then
|
||||
echo "!! /etc/lightdm not found; skipping autologin (not lightdm?)" >&2
|
||||
else
|
||||
session=i3
|
||||
[ -e /usr/share/xsessions/$session.desktop ] ||
|
||||
echo "!! /usr/share/xsessions/$session.desktop missing; check autologin-session in $LCONF" >&2
|
||||
echo "==> enabling lightdm autologin for $(id -un) into '$session'"
|
||||
sudo mkdir -p /etc/lightdm/lightdm.conf.d
|
||||
sudo tee "$LCONF" >/dev/null <<EOF
|
||||
# Installed by socktop-swipe (--autologin). Wall display: come back unattended
|
||||
# after a reboot. Remove this file to restore the login prompt.
|
||||
[Seat:*]
|
||||
autologin-user=$(id -un)
|
||||
autologin-user-timeout=0
|
||||
autologin-session=$session
|
||||
EOF
|
||||
echo " wrote $LCONF (takes effect at next boot)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- optional: i3 autostart -------------------------------------------------
|
||||
if [ "$do_i3" = yes ]; then
|
||||
i3conf="$HOME/.config/i3/config"
|
||||
|
||||
+55
-17
@@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
# Build the socktop display session: one window, N tiled panes, one per host.
|
||||
# Pane index order is the swipe order.
|
||||
# Build the socktop display session: one tmux window per GROUP, one pane per
|
||||
# host inside it. Window order and pane index order together are the swipe
|
||||
# order (see socktop-swipe).
|
||||
set -eu
|
||||
|
||||
# shellcheck source=config.env
|
||||
@@ -8,29 +9,66 @@ for c in /usr/local/etc/socktop-swipe.env "$(dirname "$0")/config.env"; do
|
||||
[ -r "$c" ] && . "$c" && break
|
||||
done
|
||||
|
||||
: "${SOCKTOP_HOSTS:?no SOCKTOP_HOSTS -- is the config installed?}"
|
||||
: "${SOCKTOP_GROUPS:=}"
|
||||
: "${SOCKTOP_HOSTS:=}"
|
||||
: "${SOCKTOP_SESSION:=socktop4}"
|
||||
: "${SOCKTOP_BIN:=socktop}"
|
||||
|
||||
WIN="$SOCKTOP_SESSION:rack"
|
||||
# Legacy form: SOCKTOP_HOSTS alone is a single tiled group.
|
||||
if [ -z "$SOCKTOP_GROUPS" ]; then
|
||||
: "${SOCKTOP_HOSTS:?no SOCKTOP_GROUPS or SOCKTOP_HOSTS -- is the config installed?}"
|
||||
SOCKTOP_GROUPS=$SOCKTOP_HOSTS
|
||||
fi
|
||||
|
||||
tmux kill-session -t "$SOCKTOP_SESSION" 2>/dev/null || true
|
||||
|
||||
first=1
|
||||
for h in $SOCKTOP_HOSTS; do
|
||||
if [ "$first" = 1 ]; then
|
||||
tmux new-session -d -s "$SOCKTOP_SESSION" -n rack "$SOCKTOP_BIN -P $h"
|
||||
first=0
|
||||
else
|
||||
# Split the most recently created pane so creation order == index order.
|
||||
tmux split-window -t "$WIN" "$SOCKTOP_BIN -P $h"
|
||||
fi
|
||||
# Label the pane with the host it is showing.
|
||||
tmux select-pane -t "$WIN.$(tmux display-message -t "$WIN" -p '#{pane_index}')" -T "$h"
|
||||
# Pane titles are set on the session, so do it up front; windows inherit.
|
||||
build_group() {
|
||||
# $1 = window name, remaining args = host names and an optional @layout
|
||||
name=$1
|
||||
shift
|
||||
layout=tiled
|
||||
hosts=
|
||||
for tok in "$@"; do
|
||||
case "$tok" in
|
||||
@*) layout=${tok#@} ;;
|
||||
*) hosts="$hosts $tok" ;;
|
||||
esac
|
||||
done
|
||||
# shellcheck disable=SC2086
|
||||
set -- $hosts
|
||||
[ $# -gt 0 ] || return 0
|
||||
|
||||
tmux select-layout -t "$WIN" tiled
|
||||
tmux select-pane -t "$WIN.0"
|
||||
if ! tmux has-session -t "$SOCKTOP_SESSION" 2>/dev/null; then
|
||||
tmux new-session -d -s "$SOCKTOP_SESSION" -n "$name" "$SOCKTOP_BIN -P $1"
|
||||
else
|
||||
tmux new-window -d -t "$SOCKTOP_SESSION" -n "$name" "$SOCKTOP_BIN -P $1"
|
||||
fi
|
||||
win="$SOCKTOP_SESSION:$name"
|
||||
tmux select-pane -t "$win.0" -T "$1"
|
||||
shift
|
||||
for h in "$@"; do
|
||||
# Split the most recently created pane so creation order == index order.
|
||||
tmux split-window -t "$win" "$SOCKTOP_BIN -P $h"
|
||||
tmux select-pane -t "$win.$(tmux display-message -t "$win" -p '#{pane_index}')" -T "$h"
|
||||
done
|
||||
tmux select-layout -t "$win" "$layout"
|
||||
tmux select-pane -t "$win.0"
|
||||
}
|
||||
|
||||
n=0
|
||||
old_ifs=$IFS
|
||||
IFS=';'
|
||||
for group in $SOCKTOP_GROUPS; do
|
||||
IFS=$old_ifs
|
||||
# shellcheck disable=SC2086
|
||||
build_group "g$n" $group
|
||||
n=$((n + 1))
|
||||
IFS=';'
|
||||
done
|
||||
IFS=$old_ifs
|
||||
|
||||
tmux select-window -t "$SOCKTOP_SESSION:g0"
|
||||
|
||||
tmux set-option -t "$SOCKTOP_SESSION" pane-border-status top
|
||||
tmux set-option -t "$SOCKTOP_SESSION" pane-border-format ' #{pane_title} '
|
||||
|
||||
+47
-17
@@ -1,14 +1,15 @@
|
||||
#!/bin/sh
|
||||
# socktop-swipe next|prev
|
||||
#
|
||||
# Walks a linear "zoom carousel" over the tiled socktop window:
|
||||
# Walks a linear "zoom carousel" over the socktop session. Each tmux window is
|
||||
# one group of hosts; within a group the line is the tiled overview, then each
|
||||
# pane zoomed full-screen in index order; then the next window's overview:
|
||||
#
|
||||
# overview <-> pane 0 <-> pane 1 <-> ... <-> pane N-1
|
||||
# (all hosts) host 1 host 2 last host
|
||||
# overview(g0) <-> g0.0 <-> ... <-> g0.N <-> overview(g1) <-> g1.0 <-> ...
|
||||
#
|
||||
# "next" moves right along that line and stops at the last pane.
|
||||
# "prev" moves back and stops at the overview. Deliberately no wrap-around:
|
||||
# on a wall display, wrapping makes it impossible to tell where you are.
|
||||
# "next" moves right and stops at the last pane of the last window. "prev"
|
||||
# moves back and stops at the first overview. Deliberately no wrap-around: on
|
||||
# a wall display, wrapping makes it impossible to tell where you are.
|
||||
#
|
||||
# This is tmux pane zoom, NOT extra socktop instances. All panes keep running
|
||||
# and stay connected while hidden, so swiping back shows current data with no
|
||||
@@ -22,10 +23,16 @@ done
|
||||
|
||||
dir=${1:?usage: socktop-swipe next|prev}
|
||||
|
||||
# Resolve the session's active window to its window id (@N) rather than assuming
|
||||
# a window name, so this works against any socktop session however it was built.
|
||||
WIN=$(tmux display-message -t "$SOCKTOP_SESSION" -p '#{window_id}' 2>/dev/null) || exit 0
|
||||
[ -n "$WIN" ] || exit 0
|
||||
# Ordered window ids (@N) of the session, and the currently active one.
|
||||
wins=$(tmux list-windows -t "$SOCKTOP_SESSION" -F '#{window_id}' 2>/dev/null) || exit 0
|
||||
[ -n "$wins" ] || exit 0
|
||||
WIN=$(tmux display-message -t "$SOCKTOP_SESSION" -p '#{window_id}') || exit 0
|
||||
|
||||
prev_win= next_win= seen=no
|
||||
for w in $wins; do
|
||||
if [ "$seen" = yes ]; then next_win=$w; break; fi
|
||||
if [ "$w" = "$WIN" ]; then seen=yes; else prev_win=$w; fi
|
||||
done
|
||||
|
||||
state=$(tmux display-message -t "$WIN" -p '#{window_zoomed_flag} #{pane_index} #{window_panes}') || exit 0
|
||||
zoomed=${state%% *}
|
||||
@@ -35,24 +42,47 @@ count=${rest##* }
|
||||
last=$((count - 1))
|
||||
|
||||
zoom_to() {
|
||||
# $1 = window id, $2 = pane index.
|
||||
# Selecting a different pane auto-unzooms, so zoom explicitly afterwards.
|
||||
tmux select-pane -t "$WIN.$1"
|
||||
tmux resize-pane -Z -t "$WIN.$1"
|
||||
tmux select-window -t "$1"
|
||||
tmux select-pane -t "$1.$2"
|
||||
tmux resize-pane -Z -t "$1.$2"
|
||||
}
|
||||
|
||||
overview() {
|
||||
# Show window $1 unzoomed.
|
||||
tmux select-window -t "$1"
|
||||
if [ "$(tmux display-message -t "$1" -p '#{window_zoomed_flag}')" = 1 ]; then
|
||||
tmux resize-pane -Z -t "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$zoomed" = 0 ]; then
|
||||
case "$dir" in
|
||||
next) zoom_to 0 ;;
|
||||
prev) : ;; # already at the overview; nothing further out
|
||||
next) zoom_to "$WIN" 0 ;;
|
||||
prev)
|
||||
# Back out of this group's overview onto the previous group's last pane.
|
||||
if [ -n "$prev_win" ]; then
|
||||
pl=$(tmux display-message -t "$prev_win" -p '#{window_panes}')
|
||||
zoom_to "$prev_win" $((pl - 1))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
case "$dir" in
|
||||
next) [ "$idx" -lt "$last" ] && zoom_to $((idx + 1)) || : ;;
|
||||
next)
|
||||
if [ "$idx" -lt "$last" ]; then
|
||||
zoom_to "$WIN" $((idx + 1))
|
||||
elif [ -n "$next_win" ]; then
|
||||
tmux resize-pane -Z -t "$WIN.$idx" # unzoom before leaving
|
||||
overview "$next_win"
|
||||
fi
|
||||
;;
|
||||
prev)
|
||||
if [ "$idx" -gt 0 ]; then
|
||||
zoom_to $((idx - 1))
|
||||
zoom_to "$WIN" $((idx - 1))
|
||||
else
|
||||
tmux resize-pane -Z -t "$WIN.$idx" # unzoom -> overview
|
||||
tmux resize-pane -Z -t "$WIN.$idx" # unzoom -> this group's overview
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -12,6 +12,7 @@ BIN="$PREFIX/bin"
|
||||
CONF="$PREFIX/etc/socktop-swipe.env"
|
||||
XCONF=/etc/X11/xorg.conf.d/99-ignore-touch-socktop-swipe.conf
|
||||
BCONF=/etc/X11/xorg.conf.d/10-no-blanking-socktop-swipe.conf
|
||||
LCONF=/etc/lightdm/lightdm.conf.d/50-autologin-socktop-swipe.conf
|
||||
|
||||
keep_config=no
|
||||
[ "${1:-}" = "--keep-config" ] && keep_config=yes
|
||||
@@ -42,6 +43,11 @@ if [ -e "$BCONF" ]; then
|
||||
echo "removed $BCONF (screen blanking returns after the next X restart)"
|
||||
fi
|
||||
|
||||
if [ -e "$LCONF" ]; then
|
||||
sudo rm -f "$LCONF"
|
||||
echo "removed $LCONF (login prompt returns at next boot)"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Left in place on purpose:"
|
||||
echo " * lisgd -- remove with: sudo make -C ~/src/lisgd uninstall"
|
||||
|
||||
Reference in New Issue
Block a user