# How OpenLogi Captures and Dispatches Live Gestures for Per-Direction Gesture Bindings

> Discover how OpenLogi captures live gestures and dispatches them for per-direction gesture bindings using a three-layer pipeline with OS-level hooks, HID++ capture, and IPC.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: internals
- Published: 2026-08-30

---

**OpenLogi implements a three-layer pipeline that converts physical gesture button holds into directional swipe events, enabling real-time per-direction gesture bindings through OS-level hooks, HID++ capture sessions, and IPC dispatch to the GUI.**

OpenLogi's gesture system transforms the dedicated HID++ gesture button found on MX-line mice (and haptic panels on newer models) into a live input modality capable of discriminating directional swipes. The AprilNEA/OpenLogi repository implements this through a sophisticated capture-dispatch architecture that processes raw pointer movement in real-time while the button remains depressed. This article examines the exact mechanism by which OpenLogi captures and dispatches live gestures for per-direction gesture bindings across macOS, Linux, and Windows platforms.

## The Three-Layer Gesture Capture Pipeline

The implementation spans three architectural layers, each responsible for a specific phase of the gesture lifecycle from hardware interrupt to application action.

### OS Hook Layer (openlogi-hook)

At the lowest level, platform-specific event taps intercept low-level mouse events before they reach the operating system's standard input queue. When the gesture button is pressed, the hook initiates a **gesture hold** state and begins forwarding subsequent pointer movements as **raw-XY** events to the processing pipeline.

The entry point resides in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs), which detects the gesture-button press and accumulates movement data. Platform-specific implementations in [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs), [`crates/openlogi-hook/src/linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/linux.rs), and the corresponding Windows module convert OS-level pointer deltas into normalized raw-XY reports.

### Capture Session Layer (openlogi-device)

The `run_capture_session` function defined in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) opens a dedicated HID++ communication channel for each connected device. This session receives the raw-XY reports generated by the hook layer and translates them into **Swipe** objects containing both a directional vector (up, down, left, right) and travel distance measured in device-specific units.

The session maintains strict state management: it only reports swipes while the gesture button remains held, automatically discarding spurious motion data upon button release to prevent phantom gestures.

### Agent Core and IPC Dispatch (openlogi-agent-core)

The capture session executes within the agent process watcher located at [`crates/openlogi-agent-core/src/watchers/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/watchers/gesture.rs). As each swipe object is produced, the agent constructs a **gesture event** and transmits it over the internal **tarpc** IPC channel to the GUI layer (either desktop application or overlay).

This architecture decouples the low-level hardware polling from the user interface, ensuring that gesture processing does not block the main application thread.

## Per-Direction Gesture Binding Configuration

OpenLogi's configuration system defines gesture mappings through the `Binding::Gesture` type in [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs). This structure encapsulates five distinct sub-bindings: one for a plain click (no directional movement) and four directional variants corresponding to cardinal swipe directions.

```rust
// crates/openlogi-core/src/binding/gesture.rs
pub enum Binding {
    Gesture {
        click: Action,
        up: Action,
        down: Action,
        left: Action,
        right: Action,
    },
    // ...
}

```

The live swipe data produced by the capture session selects the appropriate sub-binding based on accumulated directional data. If the button releases before exceeding the directional threshold, the system triggers the plain-click binding; otherwise, it executes the action associated with the dominant direction.

Default mappings are provisioned in [`crates/openlogi-core/src/binding/defaults.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/defaults.rs), registering standard gestures such as Mission Control for upward swipes and Desktop Switching for downward movements.

## Complete Gesture Flow from Hardware to Action

The end-to-end execution follows a deterministic sequence:

1. **Detection**: The OS hook identifies the gesture-button press and initiates a hold state in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs).
2. **Accumulation**: Pointer movement during the hold generates raw-XY events through platform-specific modules ([`macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/macos.rs), [`linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/linux.rs)).
3. **Translation**: The `run_capture_session` loop in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) aggregates raw data and determines the swipe direction.
4. **Dispatch**: 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) forwards a `Gesture` IPC message containing the resolved direction.
5. **Execution**: The GUI layer matches the direction against user-defined sub-bindings in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs) and executes the associated command.

This pipeline enables *live* gesture recognition: the system processes directional data in real-time, allowing distinct actions to fire immediately upon direction confirmation without requiring button release.

## Summary

- OpenLogi captures live gestures through a three-layer pipeline: OS hooks, device sessions, and IPC dispatch.
- The `run_capture_session` function in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) converts raw-XY input into directional Swipe objects while filtering noise.
- Platform-specific implementations in [`crates/openlogi-hook/src/macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/macos.rs) and [`linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/linux.rs) normalize OS-level pointer events for cross-platform consistency.
- Per-direction bindings are defined via `Binding::Gesture` in [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs), supporting five sub-actions (click + four directions).
- The tarpc IPC channel enables asynchronous communication between the low-level agent core and the GUI for responsive gesture execution.

## Frequently Asked Questions

### How does OpenLogi distinguish between a click and a directional swipe on the gesture button?

OpenLogi measures accumulated movement during the button hold period. If the button releases before the raw-XY delta exceeds the configured directional threshold, the system triggers the plain-click binding defined in `Binding::Gesture`. Movement exceeding the threshold in any cardinal direction selects the corresponding directional sub-binding.

### What happens to gesture input if the GUI process is not running?

The agent core in [`crates/openlogi-agent-core/src/watchers/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent-core/src/watchers/gesture.rs) runs independently as a background service. It maintains the capture session and buffers gesture events; if the GUI is unavailable, the events are discarded gracefully without blocking the HID++ communication channel or interfering with normal mouse operation.

### Does OpenLogi support diagonal swipes or only cardinal directions?

The current implementation in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) categorizes swipes into four cardinal directions (up, down, left, right). Diagonal movements are resolved to the dominant axis based on the greatest magnitude of displacement in either the X or Y direction.

### How does the platform-specific hook code handle different operating system event APIs?

Each platform module—[`macos.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/macos.rs), [`linux.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/linux.rs), and [`windows.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/windows.rs)—implements a common trait interface that normalizes OS-specific event taps into standardized raw-XY reports. This abstraction allows the `run_capture_session` logic to remain platform-agnostic while the hook layer handles the specifics of CGEvent for macOS, evdev for Linux, or Raw Input for Windows.