# How to Configure Button Bindings in OpenLogi TOML: Complete Syntax Guide

> Configure button bindings in OpenLogi TOML. Learn the syntax for shortcuts, hold actions, press splits, key sequences, and app overrides in your config.toml.

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

---

**Button bindings in OpenLogi are configured inside `[devices."<device>".bindings]` tables in your `~/.config/openlogi/config.toml` file, supporting simple shortcuts, hold-while-pressed actions, short/long press splits, custom key sequences, and per-application overrides.**

OpenLogi stores all user-customizable settings in a TOML configuration file, typically located at `~/.config/openlogi/config.toml`. The **button bindings configuration** allows you to remap any button on supported Logitech HID++ devices—from basic thumb buttons to gesture-enabled controls—using a declarative, human-readable syntax. This guide covers the complete schema, resolution order, and implementation details based on the OpenLogi source code.

## Button Bindings Table Structure

All button bindings reside under device-specific tables. The device identifier `<device>` can be a receiver-based path like `receiver:aabbccdd:slot:1` or a direct unit identifier like `unit:6be9d300`.

```toml
[devices."receiver:aabbccdd:slot:1".bindings]
Back = "BrowserBack"
Forward = "BrowserForward"

```

In [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs), the core function `openlogi_core::bindings::bindings_for` merges multiple binding sources into a final `BTreeMap<ButtonId, Action>` that the runtime uses to handle button events.

## Action Types and TOML Syntax

OpenLogi supports six distinct action types for button bindings. Each type uses a specific TOML structure.

### Simple Shortcuts

The most common binding: a string value maps directly to a predefined system shortcut.

```toml
[devices."receiver:aabbccdd:slot:1".bindings]
Back      = "BrowserBack"
Forward   = "BrowserForward"
Thumbwheel = "AppExpose"

```

Available shortcut names include `BrowserBack`, `BrowserForward`, `ShowDesktop`, `MissionControl`, `AppExpose`, `Undo`, `PreviousDesktop`, `NextDesktop`, and others defined in the action registry.

### Hold-While-Pressed Shortcuts

Use an **inline table** with `HoldShortcut` to activate a modifier combination only while the button remains pressed.

```toml
[devices."receiver:aabbccdd:slot:1".bindings]
MiddleClick = { HoldShortcut = "Ctrl+Space" }

```

This is implemented in the bindings parser as a distinct `Action::HoldShortcut` variant, processed by the gesture engine in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs).

### Short-Press / Long-Press Distinction

Define different actions based on press duration using `short` and `long` keys.

```toml
[devices."receiver:aabbccdd:slot:1".bindings]
DpiToggle = { short = "ShowDesktop", long = "MissionControl" }

```

The threshold between short and long press is handled by the input processing layer, with the binding resolution occurring in `bindings_for`.

### Custom Shortcuts

For arbitrary key sequences not covered by predefined shortcuts, use `CustomShortcut` with any valid key combination string.

```toml
[devices."receiver:aabbccdd:slot:1".per_app_bindings."exe:sharex.exe"]
MiddleClick = { CustomShortcut = "F1" }

```

This delegates to the platform's shortcut injection system, parsing the key sequence at runtime.

### Gesture Button Bindings

The **GestureButton** supports nested directional actions: `Click`, `Up`, `Down`, `Left`, `Right`.

```toml
[devices."receiver:aabbccdd:slot:1".bindings.GestureButton]
Click = "MissionControl"
Up    = "MissionControl"
Down  = "AppExpose"
Left  = "PreviousDesktop"
Right = "NextDesktop"

```

Gesture detection is performed by the HID++ event processor, with direction vectors mapped to these binding entries before the standard action dispatch.

## Per-Application Binding Overrides

Override global bindings when specific applications are active using `per_app_bindings."<app-id>"` tables.

```toml
[devices."receiver:aabbccdd:slot:1".per_app_bindings."com.microsoft.VSCode"]
Back = "Undo"

[devices."receiver:aabbccdd:slot:1".per_app_bindings."exe:photoshop.exe"]
Back = "StepBackward"
Thumbwheel = "Zoom"

```

Application identifiers use macOS bundle IDs (`com.microsoft.VSCode`) or Windows executable names (`exe:sharex.exe`, `exe:photoshop.exe`). The active application detection is performed by the platform integration layer in `crates/openlogi-desktop/src/state/`.

## Binding Resolution Order

When a button event occurs, OpenLogi resolves the effective action through a prioritized merge:

1. **Per-app overrides** (`per_app_bindings`) — highest priority, only when the specified application is active
2. **Device-global bindings** (`bindings`) — applied when no per-app entry exists for the active context
3. **Default bindings** — built-in hardware defaults (left-click, right-click, scroll wheel) as lowest-priority fallback

This merge logic is centralized in `openlogi_core::bindings::bindings_for` in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs), which produces the final `BTreeMap<ButtonId, Action>` consumed by the input pipeline.

## Editing Bindings via the UI Layer

The desktop application manages TOML persistence through [`crates/openlogi-desktop/src/state/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/bindings.rs). This module:

- Loads the binding map via `bindings_for`
- Exposes current assignments to the GUI for display
- Serializes user modifications back to the configuration file

The Actions Ring feature ([`crates/openlogi-desktop/src/state/action_ring.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/action_ring.rs)) respects identical resolution rules, ensuring consistent behavior across manual TOML edits and UI-driven configuration.

## Complete Configuration Example

```toml

# ~/.config/openlogi/config.toml

[devices."receiver:aabbccdd:slot:1".bindings]
Back         = "BrowserBack"
Forward      = "BrowserForward"
MiddleClick  = { HoldShortcut = "Ctrl+Space" }
DpiToggle    = { short = "ShowDesktop", long = "MissionControl" }
Thumbwheel   = "AppExpose"

[devices."receiver:aabbccdd:slot:1".bindings.GestureButton]
Click = "MissionControl"
Up    = "MissionControl"
Down  = "AppExpose"
Left  = "PreviousDesktop"
Right = "NextDesktop"

[devices."receiver:aabbccdd:slot:1".per_app_bindings."com.microsoft.VSCode"]
Back = "Undo"
Forward = "Redo"

[devices."receiver:aabbccdd:slot:1".per_app_bindings."exe:sharex.exe"]
MiddleClick = { CustomShortcut = "F1" }

```

Reference [[`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml)](https://github.com/AprilNEA/OpenLogi/blob/master/docs/config.example.toml) in the repository for a comprehensive, annotated configuration template.

## Summary

- **Button bindings in OpenLogi TOML** are defined under `[devices."<id>".bindings]` using device identifiers from HID++ enumeration
- **Six action types** are supported: simple strings, `HoldShortcut`, `short`/`long` splits, `CustomShortcut`, nested `GestureButton` tables, and per-app overrides
- **Resolution priority**: `per_app_bindings` → `bindings` → defaults, merged by `bindings_for` in [`crates/openlogi-core/src/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/bindings.rs)
- **UI synchronization** is handled by [`crates/openlogi-desktop/src/state/bindings.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/state/bindings.rs), ensuring bidirectional consistency between TOML and graphical configuration

## Frequently Asked Questions

### What is the default location for the OpenLogi configuration file?

The default path is `~/.config/openlogi/config.toml` on Linux and macOS, with platform-appropriate equivalents on Windows. OpenLogi creates this file on first launch if it does not exist, populated with commented examples from the repository's [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml).

### How do I find my Logitech device's identifier for the TOML table name?

Device identifiers are logged during OpenLogi startup and visible in the desktop application's device list. They follow patterns like `receiver:aabbccdd:slot:1` for receiver-paired devices or `unit:6be9d300` for direct USB-connected devices. The core enumeration logic resides in [`crates/openlogi-core/src/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/device.rs).

### Can I use multiple per-app bindings for the same device?

Yes. Define separate tables for each application under `[devices."<id>".per_app_bindings."<app-id>"]` with unique app identifiers. OpenLogi evaluates all matching tables when the active application changes, selecting the most specific override available. Only one override applies per button at runtime.

### Why aren't my gesture button bindings working?

Verify that your hardware supports HID++ gesture events and that `GestureButton` is correctly capitalized in the TOML key. Gesture bindings require a nested table structure with directional keys (`Click`, `Up`, `Down`, `Left`, `Right`). If the device reports gesture events but bindings fail, check `bindings_for` output in debug logs to confirm the merge produced expected `Action::Gesture` entries.