# How OpenLogi Implements Gesture Recognition on Buttons: A Technical Deep Dive

> Discover how OpenLogi implements gesture recognition on buttons. Learn about raw XY reporting, motion deltas, and gesture event emission for intuitive control.

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

---

**OpenLogi implements gesture recognition by diverting dedicated mouse buttons into raw‑XY reporting mode, accumulating incremental motion deltas while the button is held, and emitting typed `GestureDirection` events when swipe thresholds are crossed or falling back to plain clicks when released without motion.**

OpenLogi is an open‑source Rust project that enables advanced input handling for Logitech HID++ devices. The gesture recognition system transforms physical button holds into directional swipe commands by intercepting low‑level device reports and converting them into high‑level UI events.

## The Gesture Recognition Pipeline

The implementation spans three main stages across the `openlogi‑device` and `openlogi‑core` crates. The system first identifies which physical buttons act as gesture sources, then captures raw motion data while those buttons are held, and finally converts accumulated deltas into logical direction commands.

### Identifying Gesture Source Controls

In [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs), the constant `GESTURE_SOURCE_BUTTONS` (lines 188‑196) defines the HID++ control IDs that function as gesture inputs. This includes the generic gesture button found on many Logitech mice and the haptic panel button present on the MX Master 4.

### Diverting to Raw‑XY Mode

The `run_capture_session` function opens a dedicated HID++ channel and arms the identified sources for raw‑XY reporting via `arm_reprog_control`. When the user holds a gesture button, the device streams incremental X/Y deltas as `0x1b04` reports. The session registers a message listener that forwards these reports to `handle_reprog_with_gesture_buttons`, which updates the internal `CaptureAccum` state (lines 327‑340).

### Motion Accumulation and Threshold Detection

While a gesture button remains held, `handle_raw_xy` (lines 53‑59) receives delta values (`dx`, `dy`) and feeds them into a `SwipeAccumulator`. The accumulator tracks cumulative motion and determines when the user has performed an intentional swipe versus incidental movement. Once the accumulated displacement crosses the configured threshold, the system returns a specific `GestureDirection`.

## The GestureDirection Enum and Event Types

The logical output types are defined in [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs) (lines 15‑27). The `GestureDirection` enum provides five variants: `Up`, `Down`, `Left`, `Right`, and `Click`. Each variant includes stable string keys for localization and UI labeling.

When the accumulator detects a valid swipe or the button releases without sufficient motion, the system emits a `CapturedInput::Gesture(button_id, direction)` event. This high‑level structure decouples the raw hardware protocol from the application layer.

## Dispatching to User Actions

The GUI layer receives `CapturedInput` events via an unbounded channel from the agent watcher in [`crates/openlogi-agent-core/src/watchers/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/watchers/gesture.rs). For each incoming gesture event, the UI looks up the user‑defined binding for the specific `ButtonId` and `GestureDirection` pair, then executes the associated action from the binding map defined in [`crates/openlogi-core/src/binding/binding.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/binding.rs).

## Implementation Example

To integrate gesture recognition in your OpenLogi workflow, create a capture specification and handle the resulting events:

```rust
use openlogi_device::session::{CaptureSpec, run_capture_session};
use openlogi_core::binding::gesture::GestureDirection;
use openlogi_core::input::CapturedInput;

// Configure which buttons to treat as gesture sources
let spec = CaptureSpec {
    capture_thumbwheel: false,
    divert_gesture_sources: vec![reprog_controls::GESTURE_BUTTON_CID],
    divert_gesture_buttons: vec![],
    divert_buttons: vec![],
};

// Spawn the capture session
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
tokio::spawn(async move {
    run_capture_session(
        backend,
        route,
        spec,
        tx,
        shutdown_rx,
        channel_slot,
        device_io,
    ).await.expect("Capture session failed");
});

// Process high-level gestures in your UI loop
while let Some(input) = rx.recv().await {
    if let CapturedInput::Gesture(btn, dir) = input {
        match dir {
            GestureDirection::Up => println!("Button {:?} swiped up", btn),
            GestureDirection::Click => println!("Button {:?} clicked", btn),
            _ => println!("Button {:?} gesture: {:?}", btn, dir),
        }
        // Map (btn, dir) to user-configured actions
    }
}

```

## Summary

- OpenLogi identifies gesture‑capable buttons using the `GESTURE_SOURCE_BUTTONS` constant in [`gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/gesture.rs), covering both standard gesture buttons and MX Master 4 haptic panels.
- The `run_capture_session` function diverts these controls into raw‑XY mode, capturing `0x1b04` HID++ reports via `handle_reprog_with_gesture_buttons`.
- Raw deltas accumulate in the `SwipeAccumulator` until crossing a threshold, triggering `handle_raw_xy` to emit a `GestureDirection`.
- The system supports five gesture types defined in `GestureDirection`: **Up**, **Down**, **Left**, **Right**, and **Click**.
- High‑level `CapturedInput::Gesture` events flow through channels to the GUI, which looks up bindings in the per‑button gesture map to execute user‑defined actions.

## Frequently Asked Questions

### What hardware does OpenLogi support for gesture recognition?

OpenLogi supports any Logitech device that exposes gesture‑capable buttons through the HID++ protocol. This includes mice with dedicated gesture buttons and the MX Master 4 series with its haptic panel, as identified by the control IDs listed in `GESTURE_SOURCE_BUTTONS` (lines 188‑196 of [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs)).

### How does OpenLogi distinguish between a click and a swipe?

The `SwipeAccumulator` tracks incremental X/Y motion while the gesture button remains held. If the accumulated displacement exceeds the configured threshold before the button releases, the system emits a directional swipe; otherwise, it falls back to a `GestureDirection::Click` when the hold ends without sufficient motion.

### Can I customize the swipe sensitivity thresholds?

The threshold logic resides within the `SwipeAccumulator` implementation used by `handle_raw_xy` (lines 53‑59). While the core recognition pipeline handles the accumulation math, the threshold parameters are typically configurable through the `CaptureSpec` structure or device‑specific calibration settings in the OpenLogi configuration.

### What is the difference between `divert_gesture_sources` and `divert_gesture_buttons` in the capture specification?

`divert_gesture_sources` accepts HID++ control IDs that should enter raw‑XY reporting mode for gesture recognition, such as the dedicated gesture button CID. `divert_gesture_buttons` handles standard button diversions that do not require raw motion tracking. For full gesture support, you must populate `divert_gesture_sources` with the appropriate control identifiers from `GESTURE_SOURCE_BUTTONS`.