A lightweight, config-driven gesture daemon for Linux touchscreens. It translates raw multi-touch
input into arbitrary shell commands via evdev - no desktop environment
required, perfect for headless kiosk setups on Raspberry Pi and similar devices.
π‘ The name bodgestr is a portmanteau of bodge + gesture β a nod to the pragmatic, "good enough" engineering spirit behind the project.
π₯οΈ Looking for a full kiosk setup? See the kub62-ansible kiosk role for an Ansible-based deployment that includes bodgestr.
- π Swipe, tap, double-tap, long-press & pinch gesture recognition
- π₯οΈ Multi-device support - configure multiple touchscreens in one file
- ποΈ Two-tier config hierarchy: global β per-device (thresholds & gestures)
- π Automatic reconnection on USB disconnect
- π¦
.deband.rpmpackages - single install, ready to go - πͺ΅ systemd + journald logging with optional file logging and logrotate
- β‘ Single binary, minimal footprint - no runtime dependencies beyond glibc
- π§ Linux with evdev support
- π A multi-touch capable touchscreen
- π Read access to
/dev/input/event*(typically requiresrootorinputgroup) - π¦ Rust β₯ 1.85 (only for building from source)
Download the latest .deb for your architecture from
GitHub Releases:
sudo apt install ./bodgestr_*.debDownload the latest .rpm for your architecture from
GitHub Releases:
sudo dnf install ./bodgestr-*.rpmgit clone https://github.com/mzellho/bodgestr.git && cd bodgestr
cargo test # run tests
make build # release build
sudo make install # install to /usr/bin, /etc, systemdbodgestr --list-devicesEdit /etc/bodgestr/gestures.toml - register your device and enable the gestures you need:
[global]
log_level = "info"
log_file = "/var/log/bodgestr/bodgestr.log"
[global.thresholds]
swipe_time_max = 0.9
swipe_distance_min_pct = 0.15
angle_tolerance_deg = 30.0
tap_time_max = 0.2
long_press_time_min = 0.8
double_tap_interval = 0.3
tap_distance_max = 50.0
double_tap_distance_max = 50.0
pinch_threshold_pct = 0.1
[global.gestures.tap]
action = "xdotool click 1"
enabled = true
[global.gestures.long_press]
action = "notify-send 'bodgestr' 'Long press detected'"
enabled = true
[device.kiosk]
device_usb_id = "1234:5678"
enabled = true
[device.kiosk.gestures.swipe_left]
action = "xdotool key Left"
enabled = true
[device.kiosk.gestures.swipe_right]
action = "xdotool key Right"
enabled = true
[device.kiosk.gestures.swipe_up]
action = "brightnessctl set +10%"
enabled = trueAll thresholds and gesture actions follow a two-tier priority: per-device β global. Devices inherit everything from the global section - you only need to override what differs.
π See
config/gestures.example.tomlfor the full reference with all available options.
bodgestr # βΆοΈ default config (/etc/bodgestr/gestures.toml)
bodgestr /path/to/gestures.toml # βΆοΈ custom config path
bodgestr -v # π verbose / DEBUG
sudo systemctl enable --now bodgestr # π as systemd service
sudo systemctl status bodgestr # β
check status
sudo journalctl -u bodgestr -f # π follow logs| Gesture | Description |
|---|---|
swipe_left, swipe_right, swipe_up, swipe_down |
Directional swipe |
tap |
Short single touch |
double_tap |
Two taps in quick succession |
long_press |
Touch and hold |
pinch_in, pinch_out |
Two-finger pinch to zoom |
Each gesture can trigger any shell command - actions are executed via sh -c, so anything your
system can run works:
xdotool click 1 # simulate mouse click
xdotool key --clearmodifiers ctrl+Tab # keyboard shortcut
notify-send "Gesture" "Swipe detected!" # desktop notification
/usr/local/bin/my-script.sh # custom script
brightnessctl set +10% # hardware control
playerctl next # media control
brightnessctl set +10% && notify-send "Brightness" "Up" # chained commandsThe configuration file uses TOML format. Both thresholds and gestures follow the same two-tier priority: per-device β global.
Every device must be registered with its USB ID and explicitly enabled:
[device.kiosk]
device_usb_id = "1234:5678"
enabled = trueDevices inherit all global thresholds. Override per device:
[device.kiosk.thresholds]
swipe_time_max = 1.5 # allow slower swipes on this device
tap_distance_max = 80.0 # more forgiving tap radiusDevices inherit all global gestures. Override action or enabled state per device:
# Global: all devices get this tap action
[global.gestures.tap]
action = "xdotool click 1"
enabled = false
# Device: enable tap, add swipe
[device.kiosk.gestures.tap]
enabled = true
[device.kiosk.gestures.swipe_left]
action = "xdotool key Left"
enabled = trueThe project includes full debian/ packaging. Build a .deb with:
sudo apt install debhelper
dpkg-buildpackage -b -us -ucThe project includes an RPM spec in dist/rpm/. Build an .rpm with:
sudo dnf install rpm-build rpmdevtools
rpmdev-setuptree
# create source tarball and build (see ci.yml for the full steps)
rpmbuild -bb dist/rpm/bodgestr.spec| Path | Description |
|---|---|
/usr/bin/bodgestr |
Daemon binary |
/usr/lib/systemd/system/bodgestr.service |
Systemd unit |
/etc/bodgestr/gestures.example.toml |
Example configuration (reference) |
/etc/bodgestr/gestures.toml |
Active config (created on first install, never overwritten) |
/etc/logrotate.d/bodgestr |
Log rotation (daily, 7 days retention) |
Logs always go to stderr (picked up by journald when running as a systemd service).
Additionally, set log_file in [global] to write to a file:
[global]
log_file = "/var/log/bodgestr/bodgestr.log"Omit log_file to disable file logging. Both .deb and .rpm packages ship a logrotate config
for /var/log/bodgestr/bodgestr.log by default.
# Debian / Ubuntu
sudo apt remove bodgestr
# Fedora / RHEL
sudo dnf remove bodgestr
# From source
sudo systemctl disable --now bodgestr
sudo make uninstallsrc/
config.rs TOML parsing, threshold merging, gesture inheritance
event.rs Touch event classification & processing (pure logic)
recognizer.rs Gesture recognition (swipe, tap, pinch, long-press)
manager.rs Device I/O, threading, reconnect (evdev layer)
main.rs CLI entry point, logger setup
tests/
test_config.rs Config parsing, merging, error handling
test_event.rs Event pipeline, classify_event, resolve_action
test_recognizer.rs Gesture detection, thresholds, edge cases
config/ Example configuration
debian/ Debian packaging
dist/
rpm/ RPM spec
systemd/ Systemd unit file
logrotate/ Logrotate configuration
The GitHub Actions workflow runs on every push and PR:
| Stage | Description |
|---|---|
| Lint | cargo fmt --check + cargo clippy |
| Test | Tests via cargo-nextest (JUnit reporting) |
| Build | Cross-compiled release binaries for amd64 and arm64 |
| DEB | Debian packages for Bookworm & Trixie Γ amd64 / arm64 |
| RPM | RPM packages for Fedora 42 & 43 Γ x86_64 / aarch64 |
| Release | Automatic GitHub Release on version tags (v*) |
This project was created as part of a personal experiment and developed with significant assistance from AI (GitHub Copilot / Claude). All code, tests, packaging, and documentation were reviewed and validated by a human.