# Understanding Gesture Detection in OpenLogi: Configuration and Implementation

> Learn about gesture detection in OpenLogi. Discover how to configure mouse buttons for swipe actions using TOML files with global, per-device, and per-app profiles. Optimize your workflow.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: deep-dive
- Published: 2026-09-12

---

**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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.

```rust
// 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.

```toml

# 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.

```toml

# 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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.

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

```

The `Action` enum in [`crates/openlogi-core/src/binding.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/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"`:

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

```

## Key Implementation Files

The gesture system spans multiple crates with distinct responsibilities:

- **[`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs)**: Contains `SwipeAccumulator` and the HID++ report processing logic that detects gesture button presses and emits `Gesture` events.
- **[`crates/openlogi-core/src/binding.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding.rs)**: Defines `ButtonId`, `GestureDirection`, `Action`, and the `Config::effective_bindings` API for resolving hierarchical configurations.
- **[`crates/openlogi-ipc/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs)**: Handles IPC transport carrying gesture events from the agent process to the GUI overlay.
- **[`crates/openlogi-desktop/src/state/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/bindings.rs)**: Manages runtime binding changes and applies them to global and per-application state.
- **[`docs/CONFIGURATION.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/CONFIGURATION.md)**: Documents the TOML schema including `gesture_up` and `gesture_down` keys.

## 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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/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`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/bindings.rs) invokes this API and updates the hook's internal binding map to reflect the new effective configuration.