# How to Map Gestures to Actions in OpenLogi: A Complete Configuration Guide

> Learn how to map gestures to actions in OpenLogi with this complete configuration guide. Master directional swipes and clicks for custom commands.

- Repository: [Xuan Zhang/OpenLogi](https://github.com/AprilNEA/OpenLogi)
- Tags: how-to-guide
- Published: 2026-09-10

---

**OpenLogi maps mouse gestures to actions through a three-layer architecture where directional swipes (Up, Down, Left, Right) and clicks are captured in [`gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/gesture.rs), stored in TOML configuration, and dispatched via `hidpp_gesture_maps_for` in [`bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/bindings.rs) to execute specific commands like `Copy` or `VolumeUp`.**

OpenLogi is an open-source Logitech mouse configuration tool that enables advanced gesture control on devices like the MX Master series. When you map gestures to actions in OpenLogi, you translate physical directional swipes into executable commands through a runtime system that bridges raw HID++ events with user-defined configuration.

## Understanding the Gesture Mapping Architecture

OpenLogi implements gesture handling through three distinct layers that transform physical input into executed commands.

**Configuration Layer** stores per-device and per-application gesture bindings in a TOML file. Gestures are represented as a map keyed by `GestureDirection` (Up, Down, Left, Right, Click). The implementation in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) provides `Config::set_gesture_mode` and `Config::bindings_for` to manage these mappings.

**Binding Projection Layer** converts stored TOML data into runtime maps using `ButtonId → GestureDirection → Action` relationships. The `hidpp_gesture_maps_for` and `oshook_gestures_for` functions in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs) handle this projection, automatically assigning default actions to undefined directions.

**Runtime Dispatch Layer** captures raw HID++ events in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs), constructing `CapturedInput::Gesture(button, direction)` objects. The GUI then looks up the corresponding `Action` from the projected map and executes the command.

## How to Map Gestures to Actions in OpenLogi

### Enable Gesture Mode for the Device

Before mapping specific directions, you must enable **gesture mode** for the target button. This designates the button (such as the MX Master gesture button or haptic panel) as a gesture source rather than a standard input.

Use the CLI to enable gesture mode for your device:

```bash

# Enable gesture mode for device "2b042" (MX Master 2S)

openlogi config set-gesture-mode 2b042 GestureButton true

```

This command invokes `Config::set_gesture_mode` in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), which sets the `GestureDirection::Click` entry and marks the button as a gesture source.

### Configure Directional Bindings

Each directional swipe requires a separate entry under `bindings.<device>.GestureButton.<direction>` in your configuration file. OpenLogi recognizes five directions: **Up**, **Down**, **Left**, **Right**, and **Click** (the tap action).

Edit `~/.config/openlogi/config.toml` directly:

```toml
[bindings."2b042".GestureButton]
Up = "Copy"
Down = "Paste"
Left = "VolumeDown"
Right = "VolumeUp"
Click = "MissionControl"

```

Alternatively, use the CLI helper to write bindings without manual file editing:

```bash
openlogi config set-gesture-binding 2b042 GestureButton Up Copy
openlogi config set-gesture-binding 2b042 GestureButton Down Paste
openlogi config set-gesture-binding 2b042 GestureButton Click MissionControl

```

These commands call `Config::set_gesture_binding`, which updates the underlying `BTreeMap<GestureDirection, Action>` in the configuration store.

### Verify Your Configuration

Validate that your mappings project correctly using the configuration show command:

```bash
openlogi config show 2b042 GestureButton

```

The `bindings_for` function in [`bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/bindings.rs) projects the stored map into a flat `ButtonId → Action` view. Any direction left undefined will be auto-filled with the default click action via `default_binding(button)`.

### Test the Runtime Dispatch

When the capture session is active, swiping on the gesture button generates `CapturedInput::Gesture(ButtonId::GestureButton, GestureDirection::Up)` events (or the appropriate direction). The GUI looks up the action using `hidpp_gesture_maps_for` (see [`bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/bindings.rs) line 71) and dispatches the corresponding command.

## Programmatic Configuration Example

For custom scripts or extensions, you can configure gestures programmatically using the OpenLogi core library:

```rust
use openlogi_core::config::Config;
use openlogi_core::binding::{ButtonId, GestureDirection, Action};

fn main() {
    // Load user config with automatic default merging
    let mut cfg = Config::load().expect("cannot read config");

    // Enable gesture mode for the dedicated button
    cfg.set_gesture_mode("2b042", ButtonId::GestureButton, true);

    // Bind directional gestures to actions
    cfg.set_gesture_binding(
        "2b042",
        ButtonId::GestureButton,
        GestureDirection::Up,
        Action::Copy,
    );
    cfg.set_gesture_binding(
        "2b042",
        ButtonId::GestureButton,
        GestureDirection::Down,
        Action::Paste,
    );
    cfg.set_gesture_binding(
        "2b042", 
        ButtonId::GestureButton,
        GestureDirection::Left,
        Action::VolumeDown,
    );

    // Persist changes to disk
    cfg.save().expect("cannot write config");
}

```

This example demonstrates the complete workflow: loading configuration, enabling gesture mode via `set_gesture_mode`, mapping specific directions with `set_gesture_binding`, and persisting to the TOML store.

## Summary

Mapping gestures to actions in OpenLogi requires understanding the interaction between configuration storage, binding projection, and runtime capture:

- **Enable gesture mode** first using `openlogi config set-gesture-mode` to designate the button as a gesture source
- **Store bindings** in TOML format under `bindings.<device>.GestureButton` with keys for Up, Down, Left, Right, and Click
- **Project mappings** at runtime through `hidpp_gesture_maps_for` in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs)
- **Capture events** in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) using `CapturedInput::Gesture` variants
- **Apply defaults** automatically when specific directions lack explicit bindings via `default_binding(button)`

## Frequently Asked Questions

### What configuration file format does OpenLogi use for gesture mappings?

OpenLogi uses **TOML** configuration files stored at `~/.config/openlogi/config.toml`. The gesture map resides under the `[bindings.<device_id>.GestureButton]` section, where each direction (Up, Down, Left, Right, Click) maps to a string representing the action name. The `Config` struct in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) handles serialization and deserialization of these mappings.

### Can I set different gesture actions for different applications?

Yes. The configuration layer supports **per-application bindings** in addition to per-device settings. While the CLI examples show device-level configuration (`2b042`), the underlying `Config::bindings_for` method can filter bindings based on the active application context. You can define application-specific sections in the TOML file to override global gesture mappings when specific windows are focused.

### What happens if I don't define all five gesture directions?

OpenLogi automatically backfills undefined directions with the **default click action**. When `bindings_for` projects the runtime map, any missing `GestureDirection` entries receive the value returned by `default_binding(button)`. This ensures that swiping in an unconfigured direction still triggers a predictable action rather than failing silently.

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

The system treats **Click** as a distinct `GestureDirection` variant separate from directional swipes. When you press and release the gesture button without moving, the capture session in [`crates/openlogi-device/src/session/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-device/src/session/gesture.rs) generates `CapturedInput::Gesture(ButtonId::GestureButton, GestureDirection::Click)`. This allows you to bind the tap action separately from swipe gestures, typically used to open the gesture button menu or trigger Mission Control.