Understanding Gesture Detection in OpenLogi: Configuration and Implementation

Gesture detection in OpenLogi treats designated mouse buttons as gesture inputs that accumulate pointer movement to trigger swipe-like actions, configurable through TOML files supporting global, per-device, and per-application binding profiles.

OpenLogi is an open-source input management system that transforms raw HID++ mouse reports into actionable high-level events. Understanding gesture detection in OpenLogi requires examining how the hook subsystem processes button presses and pointer deltas to generate directional swipe events. The system exposes these capabilities through a plain-text TOML configuration schema that merges global defaults with application-specific overrides.

How Gesture Detection Works in OpenLogi

OpenLogi implements gesture detection through a pipeline that converts raw hardware signals into structured Gesture events. This process involves specialized button handling, movement accumulation, and timeout-based event emission.

The Hook Subsystem and HID++ Processing

The core detection logic resides in crates/openlogi-hook/src/lib.rs, which receives raw HID++ reports from compatible Logitech devices. When a button marked as a gesture button is pressed, the hook records the timestamp and initiates a capture session for subsequent pointer movement events. Unlike standard button clicks, these inputs enter a special accumulation mode where the system tracks cumulative delta values in device units rather than emitting immediate actions.

Swipe Accumulation and Direction Detection

During an active gesture session, each pointer movement updates a SwipeAccumulator structure that tracks cumulative displacement. The accumulator monitors horizontal and vertical deltas until the gesture button releases or a timeout expires. When the button releases within the valid timeframe, the accumulated motion vector is classified into a GestureDirection (up or down) based on the dominant axis and delta sign.

// crates/openlogi-hook/src/lib.rs
if let Some(dir) = swipe_accumulator.update(movement) {
    // `dir` is GestureDirection::Up or ::Down
    let action = config.gesture_binding(dir);
    ipc::send(GestureEvent { direction: dir, action });
}

The update method returns Some(GestureDirection) only when the accumulated movement exceeds thresholds required to qualify as an intentional swipe rather than accidental jitter.

Gesture Event Emission and IPC Transport

Once classified, the gesture data forwards over the local IPC channel defined in crates/openlogi-ipc/src/transport.rs to the GUI overlay and desktop components. The GestureEvent structure carries both the direction enum and the resolved action binding, allowing the frontend to update the Actions Ring and execute the mapped command. If the button releases without sufficient accumulated movement, the system may emit a standard click event instead, depending on the button's dual-mode configuration.

Configuring Gesture Bindings in OpenLogi

OpenLogi uses a hierarchical TOML configuration system defined in the openlogi_core crate, specifically through the Config::effective_bindings API in crates/openlogi-core/src/binding.rs. This system merges global bindings, per-device profiles, and per-application overrides into a unified action resolution map.

TOML Configuration Structure

Gesture bindings use the symbolic keys gesture_up and gesture_down to represent vertical swipe directions. These keys map to entries in the built-in action catalog (openlogi_core::Action), which includes predefined actions like ScrollUp, ScrollDown, or NoOp to disable the gesture.


# Global bindings for a mouse

[bindings]

# Assign the vertical swipe of the gesture button to scroll up/down

gesture_up = "ScrollUp"
gesture_down = "ScrollDown"

The values must reference valid action variants, or the configuration loader rejects the profile during the reload sequence triggered by the ReloadConfig IPC command or startup initialization.

Per-Application Binding Overrides

The configuration supports context-aware bindings through the [app.<Name>.bindings] syntax, allowing different gesture behaviors in specific applications. When the active window matches a configured application profile, Config::effective_bindings returns the merged set where per-application values override global defaults.


# Per‑application override (e.g. only in VSCode)

[app.VSCode.bindings]
gesture_up = "ZoomIn"
gesture_down = "ZoomOut"

Runtime handling in crates/openlogi-desktop/src/state/bindings.rs monitors window focus changes and reloads the effective binding map, ensuring the hook receives updated action mappings without requiring a full service restart.

Custom Shortcuts and Advanced Actions

Beyond built-in actions, gestures can trigger custom keyboard shortcuts using the Shortcut type. This allows arbitrary key combinations to execute on swipe detection.

gesture_up = { shortcut = "Ctrl+Shift+PageUp" }

The Action enum in crates/openlogi-core/src/binding.rs serializes these definitions, supporting complex chords and modifier combinations. To completely disable gesture functionality for specific contexts, map both directions to "NoOp":

[app.Terminal.bindings]
gesture_up = "NoOp"
gesture_down = "NoOp"

Key Implementation Files

The gesture system spans multiple crates with distinct responsibilities:

Summary

  • Gesture detection in OpenLogi uses a SwipeAccumulator in the hook subsystem to convert pointer movement into directional events when gesture buttons are held.
  • The system supports vertical swipe detection (up/down) through GestureDirection enums, with timeout protection against accidental triggers.
  • TOML configuration uses gesture_up and gesture_down keys to map swipes to actions, supporting both built-in commands like ScrollUp and custom Shortcut definitions.
  • Hierarchical binding resolution merges global, per-device, and per-application profiles through Config::effective_bindings in openlogi_core.
  • Runtime updates occur through IPC ReloadConfig commands, with openlogi-desktop applying changes to the active binding state without service restarts.

Frequently Asked Questions

What file contains the core gesture detection logic in OpenLogi?

The core detection logic resides in crates/openlogi-hook/src/lib.rs. This file implements the SwipeAccumulator that tracks pointer deltas while gesture buttons are held, classifies movement into GestureDirection variants, and emits Gesture events over IPC when thresholds are met.

How do I disable gestures for specific applications?

Create a per-application profile in your TOML configuration using the [app.<Name>.bindings] syntax and set both gesture directions to "NoOp". For example, [app.Terminal.bindings] with gesture_up = "NoOp" and gesture_down = "NoOp" prevents gesture activation when the terminal window is focused.

What data structure accumulates pointer movement during gesture detection?

The SwipeAccumulator structure defined in crates/openlogi-hook/src/lib.rs stores cumulative delta values in device units while a gesture button remains pressed. Its update method processes movement events and returns Option<GestureDirection> when accumulated motion qualifies as a valid swipe.

How does OpenLogi merge different binding configurations?

The Config::effective_bindings method in crates/openlogi-core/src/binding.rs resolves the active binding set by merging global defaults, per-device overrides, and per-application profiles. When the active application changes, crates/openlogi-desktop/src/state/bindings.rs invokes this API and updates the hook's internal binding map to reflect the new effective configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →