# How to Configure Gesture Bindings on Any Button with Live Capture in OpenLogi

> Learn to configure gesture bindings on any button in OpenLogi using live capture. Map directional movements to custom actions easily via the GUI capture mode.

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

---

**Gesture bindings in OpenLogi map directional movements of a held button to custom actions, configured via TOML files or captured live through the GUI's capture mode.**

OpenLogi is an open-source input remapping system for Logitech devices. Its **gesture binding** feature lets any physical button act as a gesture pad—when held, directional movements trigger different commands. This article covers both manual TOML configuration and the live capture workflow, grounded in the actual source implementation.

---

## Understanding Gesture Bindings in OpenLogi

A gesture binding consists of two components: a **gesture-owner button** (the physical button you hold down) and **directional mappings** (up, down, left, right, tap). When the owner button is pressed and held, the low-level **hook** subsystem tracks movement vectors and emits `GestureDirection` events. The **core** receives these events through IPC and resolves them to configured actions.

According to the OpenLogi source, gesture events originate in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs), travel through [`crates/openlogi-ipc/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs), and are processed by [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs) before finally being persisted via [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs).

---

## Manual Configuration via TOML

OpenLogi stores all bindings in a profile-based TOML file, typically located at `~/.config/openlogi/config.toml`. The `gesture` table exists inside each profile's `bindings` section.

### Gesture Table Structure

```toml
[bindings.my_profile.gesture]
button = "B5"           # The physical button that enters gesture mode

up = "volume_up"
down = "volume_down"
left = "prev_track"
right = "next_track"
tap = "play_pause"

```

- **`button`**: Identifies which physical button activates gesture mode. This can be any button identifier recognized by the device—common values include `"B5"`, `"G4"`, or `"G5"` on gaming mice.
- **Direction keys (`up`, `down`, `left`, `right`, `tap`)**: Map to action names defined elsewhere in the configuration. These actions may invoke system commands, emit keystrokes, or trigger OpenLogi-internal functions.

### Example: Media Control Gestures

```toml
[bindings.work_profile.gesture]
button = "G4"
up = "brightness_up"
down = "brightness_down"
left = "desktop_left"
right = "desktop_right"
tap = "mission_control"

[bindings.work_profile.actions]
brightness_up = { type = "keyseq", keys = ["XF86MonBrightnessUp"] }
brightness_down = { type = "keyseq", keys = ["XF86MonBrightnessDown"] }
desktop_left = { type = "keyseq", keys = ["Control", "Left"] }
desktop_right = { type = "keyseq", keys = ["Control", "Right"] }
mission_control = { type = "keyseq", keys = ["Command", "Up"] }

```

The configuration parser in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) deserializes this structure into a `GestureMap`, which the runtime queries when processing incoming `GestureDirection` events.

---

## Live Capture Workflow in OpenLogi Desktop

The GUI application (`openlogi-desktop`) provides a **live capture mode** that eliminates manual TOML editing. This mode streams gesture events in real-time and writes bindings directly to the active profile.

### How Live Capture Works

1. **Activation**: Select a button in the UI and enter capture mode. The GUI signals the **hook** to begin emitting `GestureDirection` events for that specific button.

2. **Event Streaming**: While the button is held, the hook in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) samples HID++ reports, calculates movement vectors, and translates them into discrete directions. These events travel through the IPC channel defined in [`crates/openlogi-ipc/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs).

3. **UI Feedback**: The GUI displays live directional indicators as the user moves their mouse or trackball. Each detected direction highlights in the interface.

4. **Binding Assignment**: When the user clicks a direction in the UI and selects an action, the core constructs a `GestureBinding` struct (defined in [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs)) and attaches it to the owner button.

5. **Persistence**: On save, [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs) serializes the updated `GestureMap` back to the TOML file.

### Capture Mode Code Flow

```rust
// Simplified representation of the GUI capture implementation
use openlogi_core::binding::gesture::{GestureBinding, GestureDirection};
use openlogi_core::profile::Profile;

fn run_capture_session(profile: &mut Profile, owner_button: String) {
    let mut capture = GestureCapture::new(owner_button.clone());
    capture.start();  // Signal hook to begin streaming events
    
    // Live preview loop
    while let Some(direction) = capture.next_event() {
        ui.highlight_direction(direction);  // Visual feedback
    }
    
    // User finalizes binding
    let binding = GestureBinding {
        up: ui.selected_action("up"),
        down: ui.selected_action("down"),
        left: ui.selected_action("left"),
        right: ui.selected_action("right"),
        tap: ui.selected_action("tap"),
    };
    
    profile.gesture_map.insert(owner_button, binding);
    profile.save();  // Delegates to bindings.rs for TOML serialization
}

```

The `GestureCapture` struct manages the lifecycle of a capture session, ensuring that events from other buttons are filtered and only the designated owner button generates gesture events.

---

## Key Source Files and Their Roles

| File | Purpose | GitHub Link |
|------|---------|-------------|
| [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) | Low-level HID++ hook that emits `GestureDirection` events from raw device reports | [View source](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-hook/src/lib.rs) |
| [`crates/openlogi-ipc/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs) | IPC channel (tarpc/bincode) carrying events between hook, GUI, and core | [View source](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-ipc/src/transport.rs) |
| [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs) | `GestureBinding` definition and `From<&GestureDirection>` conversion logic | [View source](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/binding/gesture.rs) |
| [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) | TOML deserialization for profiles, including the `gesture` table | [View source](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/config.rs) |
| [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs) | Persistence layer that writes updated bindings to disk | [View source](https://github.com/AprilNEA/OpenLogi/blob/master/crates/openlogi-core/src/bindings.rs) |
| [`docs/CONFIGURATION.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/CONFIGURATION.md) | Official documentation of the TOML schema | [View docs](https://github.com/AprilNEA/OpenLogi/blob/master/docs/CONFIGURATION.md) |

---

## Triggering Bound Gestures Programmatically

The `openlogi-inject` crate allows synthetic gesture injection for testing or automation:

```rust
use openlogi_core::binding::gesture::GestureDirection;

fn trigger_bound_action(direction: GestureDirection) {
    // Injects a gesture as if it came from the physical device
    openlogi_inject::inject_gesture(direction);
}

// Fire the "right" binding on the current gesture-owner button
trigger_bound_action(GestureDirection::Right);

```

This capability is used by the test suite in `crates/openlogi-core/tests/` to verify that gesture bindings resolve to correct actions without requiring physical hardware.

---

## Summary

- **Gesture bindings** associate directional movements of a held button with actions, configured in the `gesture` table of any profile.
- **Manual TOML editing** works for precise control: set `button` to any button ID, then assign actions to each direction.
- **Live capture mode** in `openlogi-desktop` streams `GestureDirection` events through hook → IPC → core, letting you assign bindings visually.
- The event pipeline spans [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs), [`crates/openlogi-ipc/src/transport.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/transport.rs), and [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs), with persistence handled by [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs).
- Any button can become a gesture-owner button, and any action defined in the profile can be bound to any direction.

---

## Frequently Asked Questions

### What buttons can be used as gesture-owner buttons?

Any button recognized by your Logitech device can serve as a gesture-owner button. The `button` field in the TOML accepts standard identifiers like `"B5"` (thumb button), `"G4"`, `"G5"` (G-series buttons), or device-specific codes. The hook subsystem in [`crates/openlogi-hook/src/lib.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-hook/src/lib.rs) treats all buttons uniformly once configured.

### Why aren't my gesture bindings triggering?

Three common causes: the **hook is not running** (required to emit `GestureDirection` events), the **owner button is misidentified** in the `button` field, or the **action names are undefined** in the profile. Verify that `openlogi-hook` has necessary permissions for low-level input access, and check that action references match keys in your `[bindings.<profile>.actions]` table.

### How do I disable live capture without saving?

The live capture UI provides a **Discard** button that terminates the capture session without invoking `profile.save()`. This prevents [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs) from writing to the TOML file, leaving your configuration unchanged.

### Can I bind multiple gesture-owner buttons in the same profile?

Yes. The `gesture_map` in [`crates/openlogi-core/src/binding/gesture.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/binding/gesture.rs) supports multiple entries—each distinct `button` value can have its own `GestureBinding`. This allows, for example, one button for media gestures and another for window management gestures within the same profile.