Commit Graph

14 Commits

Author SHA1 Message Date
jasonwitty 9226224507 Set window options per window; label panes where programs cannot clobber them
Two bugs found deploying to the LattePanda, both of which v1 also had.

Window options do not propagate from the session and new windows do not
inherit them. `set-option -t <session> pane-border-status` quietly applies to
whichever window happens to be current, so only ONE window ever got pane
borders -- v1's rack display has had unlabeled borders on two of its three
windows this whole time and nobody noticed, because the window that got them
was the one usually on screen. This is the same trap that makes remain-on-exit
useless here. pane-border-status, pane-border-format and allow-rename are now
set per window in place(), and a test asserts every window has them.

Pane labels no longer use `select-pane -T`. The pane *title* is writable by
whatever runs in the pane: unifly probes for Kitty graphics support on startup
and tmux consumed part of that probe as a title change, so the border read
"Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA" instead of "unifly". Labels now live in a
pane-scoped user option, @socktop_label, which no escape sequence can reach;
pane-border-format falls back to the title if it is somehow unset.

Also: `terminal:` now expands a leading ~/ like `binaries:` already did. A
window manager's PATH rarely includes ~/.cargo/bin, so a full path is the usual
answer there and should not have to be spelled out longhand.

Build cost corrected from guesses to measurements on the LattePanda (Atom
x5-Z8350, 4 cores, 1.9 GB, no swap, toolchain already present): 108 seconds,
peak 1.1 GB, 103 MB target directory, 946 KB binary. The README said twenty
minutes and the installer budgeted 600 MB; both were wrong.

Integration tests now use one tmux session name each -- cargo runs them in
parallel and they were tearing down each other's server state.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 13:24:16 -07:00
jasonwitty ce6acec299 v2 release prep: installer, README, packaging, notes; retire the v1 scripts
Installer rewritten around a preflight: distro, package manager, display
manager, window manager, terminal, tmux, cargo, git, screen locker, touch
device, device permissions and free disk are all checked BEFORE anything is
installed, and the total cost is printed once for a single confirmation.
Prompts read /dev/tty so they still work when the script is piped from curl,
and fall back to defaults with a notice when there is no terminal at all.

Several "[ test ] && action" statements were set -e landmines: under set -e an
AND-OR list that ends up false aborts the script, so a box with no lightdm, no
i3 or nothing to install would have exited silently partway through detection
-- which is exactly the fresh-Debian case the installer exists for. Rewritten
as if-statements and verified against a stripped PATH with no tmux, cargo, git
or package manager present. Also fixed cargo detection reporting blank instead
of NOT INSTALLED: the status of `cargo --version | cut` is cut's, and cut
succeeds on empty input, so the fallback never fired.

Device access now defaults to a udev rule matching touchscreens only, rather
than the input group, which grants access to every input device including the
keyboard and needs a full logout.

README rewritten for someone who has not seen the project: what the photo
shows, the hardware, install, then a config built up step by step, each step
with the YAML and the resulting map. Every example is verified verbatim
against the binary, and every relative link resolves. The mechanism and the
reasoning move to notes/: DESIGN.md, HARDWARE-NOTES.md, V1-BASH.md, TODO.md.

cad/README.md was a verbatim copy of the one inside
geeekpi_rack_adapter_release_v1/, so every path in it -- including the
screenshot -- was broken from where it sits. Corrected to its own level, and
it now states once that the 9-inch screen, the 10-inch mini-rack mount and the
19-inch rack are three different measurements.

The v1 shell implementation is removed; it stays recoverable at tag v1.2 and
notes/V1-BASH.md carries the setting-by-setting migration table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 13:08:42 -07:00
jasonwitty 9f082b52b7 Rust core: YAML grid config, navigation state machine, evdev input
Replaces the three shell scripts' logic with one binary. tmux stays the
pane engine; src/session/ is the only module that knows that.

The grid model: coordinates are sparse ordinals, so only their sort order
matters and a socktop group is one cell however many hosts it holds.
Horizontal movement walks a cell's sub-sequence (tiled overview, then each
host zoomed) and leaves only after the last one; entry direction decides
whether you land on the first or last sub-screen. Vertical movement returns
to where you were in that row, and snaps to the nearest column only on the
first visit.

Notable details found while building:

* Unquoted "at: 0x0" is hexadecimal 0 to YAML, and "1x0" is not valid hex,
  so only the row-0 entries would break. Deserialization catches the integer
  case and names the fix.
* Panes are addressed by tmux id, never index, and each command is wrapped
  so the pane outlives it. v1's remain-on-exit cannot do this: it is a
  per-window option that new windows do not inherit, so a monitor that exits
  during construction destroys its window and the next split fails with
  "no current target". Now a dead monitor stays on screen with its status.
* Contact-count averaging, not summing: a panel reporting one finger as
  three contacts must not look like three times the travel.
* Movement subcommands wait for the move to happen and report where they
  landed, so they are scriptable rather than fire-and-forget.

36 tests: the grid model, the gesture classifier and an end-to-end pass
against a real tmux server.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 13:01:08 -07:00
jasonwitty 9f4bcec250 Add the 19" rack adapter CAD package and the v2 plan
cad/ holds the left/right side adapters that mount the GeeekPi 9" 3U
touchscreen (a 10" mini-rack part) into a standard 19" rack: OpenSCAD
source, STLs, Anycubic Mega Pro gcode, and SendCutSend DXFs for the
sheet-metal version. Geometry is physically tested in a 19" rack.

notes/PLAN-v2.md is the reviewed plan for the Rust rewrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 12:44:17 -07:00
jasonwitty dfd39d1914 README: use a video tag for the demo (Gitea won't render image-syntax mp4)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 15:57:19 -07:00
jasonwitty aca5ee8412 README: add rack photo and swipe demo video
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 15:56:35 -07:00
jasonwitty 10bf29ede0 Hide the idle X pointer on the wall display (unclutter-xfixes)
With X ignoring the touch panel nothing ever moves the pointer, so it sits
at screen centre forever. --i3 installs unclutter-xfixes and autostarts it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 12:41:35 -07:00
jasonwitty 8380cc3793 README: document phantom display outputs (LattePanda DSI-1)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 12:14:32 -07:00
jasonwitty a492682a51 i3 autostart: open the dashboard fullscreen on the primary output
No keyboard needed after a reboot, and a second connected display can no
longer capture the window.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 11:05:53 -07:00
jasonwitty c628c45291 Add swipe-down aux screen (SOCKTOP_AUX_CMD)
A separate tmux window running any TUI, reached with a vertical swipe from
any carousel screen; swiping up returns to the exact window/zoom you left.
The aux window is excluded from the horizontal carousel and horizontal
swipes on it are inert. Bound only when SOCKTOP_AUX_CMD is set.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 10:33:26 -07:00
jasonwitty 818b09762e 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>
2026-08-28 09:53:39 -07:00
jasonwitty c5b8d04b84 Add --noblank, and fix install.sh exiting early when no relogin is needed
Wall displays must never blank. --noblank applies two layers: an Xorg
ServerFlags snippet with all four timeouts at 0 (covers the DM greeter, and
survives reboots) and an xset line in the i3 autostart (a session or DM can
re-enable the screensaver after X starts). Either alone is insufficient.

Documents the trap that cost time here: `sudo xset ...` targets root's X
connection and silently does nothing, so a `sudo bash noblank.sh` wrapper looks
like it worked while blanking stays fully armed.

Also fixes a latent bug: `[ "$relogin" = yes ] && echo ...` as the final
statement made install.sh exit non-zero under `set -e` -- and skip the closing
instructions -- whenever a relogin was not required.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 00:47:36 -07:00
jasonwitty 9a47fd47a7 Enforce single-instance in socktop-gestures instead of documenting it
The documented restart was `pkill -x lisgd && socktop-gestures &`. pkill exits
non-zero when it matches nothing, so `&&` short-circuits and the daemon
silently does not start when none was running -- you swipe, get nothing, and
reasonably blame the config change you just made. socktop-gestures exec'd lisgd
with no guard, so nothing downstream caught it.

Rather than only fixing the one-liner, move the invariant into the tool: it now
exits non-zero if a daemon is already running, and --replace stops the old one
first (waiting for it to actually exit). This is the failure the README itself
flags as gotcha 3, so a false success there is expensive.

Verified against a live daemon: refusal, --help, bad-option exit codes, and a
clean pid handover under --replace with the carousel still working.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 02:29:36 -07:00
jasonwitty 18ddd39184 Initial commit: swipe-to-zoom touchscreen navigation for socktop
Tiled socktop dashboard where a right-to-left swipe zooms into each host
full-screen and left-to-right walks back out.

The carousel is tmux pane zoom rather than separate socktop instances, so
there is one process per host: half the processes, no reconnect delay when
swiping back, and constant polling load on the monitored hosts.

Gestures come from lisgd because libinput emits gesture events only for
touchpads, never touchscreens, which rules out libinput-gestures entirely.

Documents the four gotchas found while building this: panels reporting 2-3
contacts for a one-finger swipe, X needing to ignore the panel so the
terminal and socktop stop competing for the same touches, tmux mouse mode
being incompatible with swipes, and lisgd not grabbing the device
exclusively so two instances double-fire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 01:22:23 -07:00