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

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.

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

In 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.

[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.

[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.

Short-Press / Long-Press Distinction

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

[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.

[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.

[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.

[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, 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. 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) respects identical resolution rules, ensuring consistent behavior across manual TOML edits and UI-driven configuration.

Complete Configuration Example


# ~/.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/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
  • UI synchronization is handled by 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.

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.

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →