Files
socktop-swipe/src/doctor.rs
T
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

72 lines
2.4 KiB
Rust

//! Touch diagnostics. Replaces v1's tools/diag.sh and tools/find-device.sh.
//!
//! The question it exists to answer is "the panel works, so why does nothing
//! happen?" -- which in v1 meant reading lisgd's `Cfg(f=1) <=> Evt(f=2)` output
//! and knowing that meant the contact count. Here the answer is printed in
//! English instead.
use anyhow::{bail, Result};
use crate::config::Config;
use crate::input::{list_touchscreens, Event, Touchpanel};
pub fn list() -> Result<()> {
let found = list_touchscreens();
if found.is_empty() {
bail!(
"no multitouch devices found under /dev/input/by-id/.\n\
If the panel is plugged in, you may not have permission to read the \
devices: install the udev rule or join the 'input' group and log back in."
);
}
println!("Multitouch devices:\n");
for (path, name) in found {
println!(" {name}\n {path}\n");
}
println!("Put the path in touch.device. Always the by-id path -- eventN numbers");
println!("get reshuffled on reboot or USB re-enumeration.");
Ok(())
}
pub fn run(cfg: &Config) -> Result<()> {
println!("Watching {}", cfg.touch.device);
println!(
" panel {}x{}, threshold {}px, leniency {}\u{b0}, contacts accepted: {:?}",
cfg.touch.width, cfg.touch.height, cfg.touch.threshold, cfg.touch.leniency, cfg.touch.fingers
);
println!(
" grab: {}\n",
if cfg.touch.grab {
"yes -- X will not see these touches"
} else {
"no -- X also receives these touches"
}
);
println!("Swipe left, right, up and down. Ctrl-C when done.\n");
let mut panel = Touchpanel::open(&cfg.touch)?;
let g = &cfg.gestures;
panel.run(|ev| {
match ev {
Event::Swipe(s) => {
let action = match s.direction {
d if d == g.forward => "forward",
d if d == g.back => "back",
d if d == g.up => "up",
d if d == g.down => "down",
_ => "not bound to anything",
};
println!(
" {:?} swipe, {} contact(s) -> {action}",
s.direction, s.fingers
);
}
Event::Discarded(dir, why) => {
println!(" {dir:?} swipe ignored: {why}");
}
}
true
})
}