RepoPilotOpen in app →

iberianpig/fusuma

Multitouch gestures with libinput driver on Linux

Healthy

Healthy across all four use cases

Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 1w ago
  • MIT licensed
  • CI configured
Show 2 more →
  • Tests present
  • Solo or near-solo (1 contributor active in recent commits)

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/iberianpig/fusuma)](https://repopilot.app/r/iberianpig/fusuma)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/iberianpig/fusuma on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: iberianpig/fusuma

Generated by RepoPilot · 2026-05-10 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/iberianpig/fusuma shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across all four use cases

  • Last commit 1w ago
  • MIT licensed
  • CI configured
  • Tests present
  • ⚠ Solo or near-solo (1 contributor active in recent commits)

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live iberianpig/fusuma repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/iberianpig/fusuma.

What it runs against: a local clone of iberianpig/fusuma — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in iberianpig/fusuma | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch main exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 40 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>iberianpig/fusuma</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of iberianpig/fusuma. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/iberianpig/fusuma.git
#   cd fusuma
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of iberianpig/fusuma and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "iberianpig/fusuma(\\.git)?\\b" \\
  && ok "origin remote is iberianpig/fusuma" \\
  || miss "origin remote is not iberianpig/fusuma (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
  && ok "default branch main exists" \\
  || miss "default branch main no longer exists"

# 4. Critical files exist
test -f "exe/fusuma" \\
  && ok "exe/fusuma" \\
  || miss "missing critical file: exe/fusuma"
test -f "lib/fusuma/plugin/manager.rb" \\
  && ok "lib/fusuma/plugin/manager.rb" \\
  || miss "missing critical file: lib/fusuma/plugin/manager.rb"
test -f "lib/fusuma/plugin/inputs/libinput_command_input.rb" \\
  && ok "lib/fusuma/plugin/inputs/libinput_command_input.rb" \\
  || miss "missing critical file: lib/fusuma/plugin/inputs/libinput_command_input.rb"
test -f "lib/fusuma/plugin/parsers/libinput_gesture_parser.rb" \\
  && ok "lib/fusuma/plugin/parsers/libinput_gesture_parser.rb" \\
  || miss "missing critical file: lib/fusuma/plugin/parsers/libinput_gesture_parser.rb"
test -f "lib/fusuma/config.rb" \\
  && ok "lib/fusuma/config.rb" \\
  || miss "missing critical file: lib/fusuma/config.rb"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 40 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~10d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/iberianpig/fusuma"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Fusuma is a Linux gesture recognition daemon that translates multitouch touchpad events (swipes, pinches, rotations, holds) into configurable actions via the libinput driver. It reads raw input events from /dev/input/ and maps gesture patterns defined in YAML config files to shell commands or keyboard shortcuts, enabling users to control their desktop with intuitive touchpad gestures. Modular plugin architecture housed in lib/fusuma/plugin/ with base detector classes (detectors/) for gesture types (swipe, pinch, rotate, hold), event buffers (buffers/) that accumulate raw input, a YAML config system (config/) with context matching and searching, and a main entry point at exe/fusuma. Device detection via lib/fusuma/device.rb and libinput command wrapper at lib/fusuma/libinput_command.rb.

👥Who it's for

Linux desktop users (especially laptop owners with multitouch touchpads) who want gesture-based window management and application control without proprietary touchpad drivers. Contributors are typically Ruby developers interested in hardware input handling or gesture recognition systems.

🌱Maturity & risk

Actively maintained and production-ready. The project has gem releases via gem-push CI, uses RSpec for testing (.rspec present), maintains code quality checks (Reek, RuboCop, Standard), and shows ongoing work with GitHub workflows. However, as a gem with ~228KB Ruby code and a single primary maintainer (IBERIANpig), it relies on user community feedback for edge cases.

Low-to-moderate risk. Primary risks: (1) Single maintainer dependency—all changes flow through IBERIANpig; (2) Direct /dev/input/ access requires INPUT group membership, which exposes all device input to the user; (3) Tight coupling to libinput version (requires 1.0+) and Linux kernel input subsystem versions; (4) No lock file visible in file list, so dependency versions may drift. Mitigated by active CI and test coverage.

Active areas of work

Cannot infer specific current work from file list alone, but the .github/workflows/main.yml and gem-push.yml indicate active CI/CD. The presence of .github/dependabot.yml suggests automated dependency updates are enabled. Recent work likely involves detector improvements (4 detector classes present) and config parsing robustness.

🚀Get running

git clone https://github.com/iberianpig/fusuma.git
cd fusuma
bundle install
bundle exec exe/fusuma

Pre-requisite: sudo gpasswd -a $USER input && newgrp input to grant input device read permission, then install libinput-tools (e.g., sudo apt-get install libinput-tools ruby on Ubuntu).

Daily commands:

bundle exec exe/fusuma

Fusuma is a daemon; runs in foreground and watches /dev/input/event* for multitouch events. Requires valid ~/.config/fusuma/config.yml (example at lib/fusuma/config.yml). For development: bin/setup (from Rakefile convention), then bundle exec rspec to run tests.

🗺️Map of the codebase

  • exe/fusuma — Entry point that initializes the Fusuma application and orchestrates the plugin manager startup.
  • lib/fusuma/plugin/manager.rb — Core plugin orchestration system that loads, initializes, and manages all plugins (inputs, parsers, detectors, executors).
  • lib/fusuma/plugin/inputs/libinput_command_input.rb — Primary input layer that captures raw libinput gesture data from touchpads and communicates with the gesture pipeline.
  • lib/fusuma/plugin/parsers/libinput_gesture_parser.rb — Parses raw libinput output into structured gesture events that detectors consume.
  • lib/fusuma/config.rb — Configuration loader and accessor that defines how gestures map to actions via YAML files.
  • lib/fusuma/plugin/detectors/detector.rb — Abstract base for all gesture detectors (swipe, pinch, rotate, hold) that recognize patterns and emit events.
  • lib/fusuma/plugin/executors/command_executor.rb — Executes shell commands or system actions triggered by recognized gestures.

🛠️How to make changes

Add a New Gesture Detector

  1. Create a new detector class inheriting from Fusuma::Plugin::Detectors::Detector in lib/fusuma/plugin/detectors/ (lib/fusuma/plugin/detectors/swipe_detector.rb)
  2. Implement the required methods: #detect_from_event to recognize patterns, and #estimate_current_event to emit detected gestures (lib/fusuma/plugin/detectors/detector.rb)
  3. Register your detector in the plugin manager by updating YAML plugin configuration or by auto-discovery if placed in the detectors directory (lib/fusuma/plugin/manager.rb)
  4. Define gesture mappings in the user's YAML config file to bind detector output to commands (lib/fusuma/config.rb)

Add a New Input Plugin

  1. Create a new input class inheriting from Fusuma::Plugin::Inputs::Input in lib/fusuma/plugin/inputs/ (lib/fusuma/plugin/inputs/input.rb)
  2. Implement #setup and #each to read from your data source (file, socket, API, etc.) and yield event records (lib/fusuma/plugin/inputs/libinput_command_input.rb)
  3. Create a corresponding .yml file with plugin metadata in the same directory for configuration schema (lib/fusuma/plugin/inputs/libinput_command_input.yml)
  4. Plugin manager auto-discovers and loads your input class; test via exe/fusuma with appropriate config (lib/fusuma/plugin/manager.rb)

Add a New Custom Action/Executor

  1. Create a new executor inheriting from Fusuma::Plugin::Executors::Executor in lib/fusuma/plugin/executors/ (lib/fusuma/plugin/executors/executor.rb)
  2. Implement #execute to handle the detected gesture event and trigger your custom action (lib/fusuma/plugin/executors/command_executor.rb)
  3. Add executor mappings in the user's YAML config under the gesture action definition (lib/fusuma/config.rb)
  4. Restart fusuma to load the executor; logs are available via MultiLogger for debugging (lib/fusuma/multi_logger.rb)

Add a Custom Gesture Filter or Pre-processor

  1. Create a filter class inheriting from Fusuma::Plugin::Filters::Filter in lib/fusuma/plugin/filters/ (lib/fusuma/plugin/filters/filter.rb)
  2. Implement #filtered? to define conditions under which gestures should be ignored or suppressed (lib/fusuma/plugin/filters/libinput_device_filter.rb)
  3. Plugin manager automatically loads filters; they are applied before detectors process events (lib/fusuma/plugin/manager.rb)
  4. Configure filter parameters in YAML config to control behavior per gesture or device (lib/fusuma/config.rb)

🔧Why these technologies

  • libinput — Linux-standard library for device input; provides consistent multi-touch gesture detection across trackpads and touchscreens without kernel driver dependency
  • Ruby — High-level scripting language enabling rapid plugin development; RubyGems distribution simplifies installation and dependency management
  • YAML configuration — Human-readable config format allows end-users to customize gesture mappings without code changes
  • Shell command execution (xdotool, custom scripts) — undefined

🪤Traps & gotchas

  1. INPUT group membership required: Default installation works only after sudo gpasswd -a $USER input && newgrp input; CI/tests may mask this if run as root or in permissive container. 2. libinput version lock: Code assumes libinput ≥1.0; older systems will fail silently at libinput_command calls. 3. Device enumeration timing: lib/fusuma/device.rb may race with udev if touchpad hotplugged; no visible hot-reload handler in file list. 4. YAML duplication checker (lib/fusuma/config/yaml_duplication_checker.rb) suggests config parser can fail on invalid YAML syntax; error messages may be cryptic. 5. Ruby version: No explicit version lock visible; ensure Ruby ≥2.5 (common for gems using keyword args in detectors).

🏗️Architecture

💡Concepts to learn

  • libinput Protocol & Event Types — Fusuma consumes raw input events from libinput (finger touch, motion, pressure); understanding event structure (TOUCH_DOWN, TOUCH_MOTION, TOUCH_UP with x/y/pressure payloads) is essential for debugging gesture detection failures.
  • Gesture Buffer / Temporal Windowing — Raw input is noisy and must be buffered over time (typically 100–500ms) before a gesture type can be confidently detected; gesture_buffer.rb implements this sliding window, and threshold tuning is a core user pain point.
  • State Machine for Gesture Detection — Each detector (Swipe, Pinch, Rotate) is implicitly a finite state machine (e.g., IDLE → TOUCHING → MOVING → COMPLETED); understanding state transitions helps reason about why gestures are missed or false-triggered.
  • YAML Config with Context Matching — Fusuma's power lies in its context-aware config (context_matcher.rb): same gesture can map to different actions depending on app name, language, or custom predicates; this is more sophisticated than simple keymap layers.
  • Linux Input Subsystem & /dev/input/event* — Fusuma reads from kernel input device files directly; understanding evdev protocol (struct input_event: timeval + type + code + value) explains why permissions, buffering, and timing are critical.
  • Plugin Architecture via Inheritance — Detectors and buffers extend lib/fusuma/plugin/base.rb; the plugin model allows third-party gesture types without forking—understanding the base class protocol is essential for extending Fusuma.
  • Multitouch Coordinate Geometry (Touch Tracking) — Pinch and rotate detectors must track multiple finger positions over time and compute distances/angles; understanding 2D geometric transforms (distance formula, arctangent) is needed to debug or optimize these detectors.
  • bulletmark/libinput-gestures — Alternative libinput gesture daemon written in Python; similar use case but different plugin ecosystem and config format.
  • wayland-project/libinput — Official libinput library repository; Fusuma wraps its command-line tools—understanding libinput internals helps debug gesture recognition edge cases.
  • iberianpig/fusuma-plugin-keypress — Official Fusuma plugin extending gesture→keypress mapping; example of how third-party plugins hook into Fusuma's detector architecture.
  • xdotool/xdotool — Common companion tool for Fusuma actions; many config files invoke xdotool to send keyboard events or move windows based on gestures.

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add comprehensive unit tests for lib/fusuma/config/context_matcher.rb

The context_matcher.rb file is critical for matching gesture contexts but appears to lack dedicated test coverage. This is a core piece of the gesture recognition pipeline that deserves thorough testing. Adding tests would improve reliability of context-based gesture filtering and catch regressions in complex YAML configuration matching logic.

  • [ ] Create spec/fusuma/config/context_matcher_spec.rb with tests for various context matching scenarios
  • [ ] Test edge cases like nested context structures, missing keys, and wildcard matching patterns
  • [ ] Verify integration with lib/fusuma/config/yaml_duplication_checker.rb to ensure context validity
  • [ ] Run against existing RSpec configuration (.rspec file) and CI workflow (main.yml)

Add detector-specific integration tests in lib/fusuma/plugin/detectors/

The repo has 4 gesture detectors (swipe, pinch, rotate, hold) but the test structure suggests minimal coverage for detector interactions. Adding integration tests would validate that detectors correctly process gesture events from lib/fusuma/plugin/events/ and properly buffer state via lib/fusuma/plugin/buffers/gesture_buffer.rb.

  • [ ] Create spec/fusuma/plugin/detectors/integration_spec.rb with tests for each detector type
  • [ ] Test detector output flows into command executors (lib/fusuma/plugin/executors/command_executor.rb)
  • [ ] Validate gesture_buffer state transitions during multi-touch sequences
  • [ ] Verify detector behavior with mock libinput events from lib/fusuma/plugin/inputs/libinput_command_input.yml

Implement type checking with Steep and add type signatures to lib/fusuma/plugin/manager.rb

The repo has Steepfile configured for type checking but lib/fusuma/plugin/manager.rb is the orchestrator for all plugin types (inputs, filters, detectors, executors, buffers, parsers) and likely has complex interactions that would benefit from type safety. This would improve maintainability and catch plugin compatibility issues early.

  • [ ] Add RBS type signatures for lib/fusuma/plugin/manager.rb covering plugin registration and execution flow
  • [ ] Ensure type signatures properly define plugin base class contracts from lib/fusuma/plugin/base.rb
  • [ ] Run Steep type checker (already configured) to validate the implementation
  • [ ] Document any new generic types in CONTRIBUTING.md for future plugin developers

🌿Good first issues

  • Add integration tests for lib/fusuma/config/yaml_duplication_checker.rb: currently no spec file visible for this critical validation module; write tests for common YAML errors (duplicate keys, circular includes) to catch config bugs early.
  • Document detector lifecycle in lib/fusuma/plugin/detectors/detector.rb: add RDoc comments explaining the event→gesture interface (what data does detect(event) receive? what does it return?). This unblocks contributors wanting to implement new gesture types like three-finger swipes.
  • Add device hot-plug detection to lib/fusuma/device.rb: currently no visible handler for touchpad connect/disconnect; implement a watcher (via inotify or udev) to refresh device list without daemon restart, increasing robustness on laptops with docking stations.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 43a7eb0 — Merge pull request #357 from iberianpig/issue356 (iberianpig)
  • a33e667 — fix(ci): pin rbs to < 4.0 to work around rbs-inline incompatibility (iberianpig)
  • 55a01e4 — docs: add systemd user service autostart method (iberianpig)
  • 2c12139 — v3.12.0 (iberianpig)
  • 0cc7919 — docs: fix typos and improve wording in README (iberianpig)
  • 465aada — Merge pull request #352 from iberianpig/feature/multiple-contexts (iberianpig)
  • 3e7eb4d — test: add specs for search_with_context method (iberianpig)
  • e3e8803 — fix: skip no-context blocks when searching with specific context (iberianpig)
  • a46a827 — fix: support OR condition in search_with_context (iberianpig)
  • f874963 — feat: integrate ContextMatcher into Searcher (iberianpig)

🔒Security observations

The Fusuma codebase presents moderate security risks primarily around command execution, YAML deserialization, and input validation. As a system-level gesture recognition tool requiring elevated privileges, the main concerns are: (1) unsafe command execution from configuration files, (2) potential YAML deserialization vulnerabilities, and (3) insufficient validation of device inputs. The codebase lacks explicit input sanitization mechanisms and security validation frameworks. No hardcoded secrets were detected in the provided structure. Remediation should focus on implementing strict input validation, using safe deserialization practices, and adding security controls around external command execution.

  • High · Command Injection Risk in Command Executor — lib/fusuma/plugin/executors/command_executor.rb. The codebase includes lib/fusuma/plugin/executors/command_executor.rb which executes user-defined commands from configuration files. If configuration files are not properly validated, arbitrary command execution could occur through unsanitized YAML configuration inputs. Fix: Implement strict input validation and sanitization for all commands loaded from configuration files. Use allowlists for permitted commands, avoid shell interpretation, and use array-based command execution instead of string concatenation.
  • High · YAML Deserialization Risk — lib/fusuma/config.rb, lib/fusuma/config/yaml_duplication_checker.rb. The application loads YAML configuration files (lib/fusuma/config.rb, lib/fusuma/config/yaml_duplication_checker.rb) which could be vulnerable to unsafe deserialization attacks if user-supplied YAML is processed without proper safeguards. Fix: Use YAML.safe_load() instead of YAML.load() to prevent arbitrary code execution. Explicitly define permitted classes if complex object deserialization is required.
  • Medium · Insufficient Input Validation on Device Selection — lib/fusuma/plugin/filters/libinput_device_filter.rb, lib/fusuma/device.rb. The libinput device filter (lib/fusuma/plugin/filters/libinput_device_filter.rb) and device management (lib/fusuma/device.rb) accept device identifiers from external input without apparent validation, potentially allowing device enumeration or path traversal attacks. Fix: Validate device paths against expected patterns, implement device whitelisting, and reject suspicious device identifiers. Use realpath() to prevent path traversal.
  • Medium · Missing Logging Security Controls — lib/fusuma/multi_logger.rb. The multi-logger component (lib/fusuma/multi_logger.rb) may output sensitive gesture data or configuration information to logs without apparent sanitization or access controls. Fix: Implement log redaction for sensitive data, restrict log file permissions (0600), and ensure logs are not world-readable. Consider implementing secure log rotation.
  • Medium · External Process Execution — lib/fusuma/custom_process.rb, lib/fusuma/libinput_command.rb. The custom_process.rb (lib/fusuma/custom_process.rb) and libinput_command.rb (lib/fusuma/libinput_command.rb) execute external processes. Without proper error handling and exit code validation, this could lead to silent failures or security bypasses. Fix: Validate all process exit codes, implement timeout mechanisms, capture stderr separately, and log all executed commands. Use parameterized execution to avoid shell interpretation.
  • Low · Missing Security Headers in Configuration Examples — lib/fusuma/plugin/inputs/libinput_command_input.yml. Configuration files may expose sensitive paths or privilege requirements without documentation of security implications. Fix: Add security notes to configuration examples documenting required privileges, file permissions, and potential security implications.

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.