# How to Configure Button Remapping in OpenLogi: A Complete TOML Guide

> Learn to configure button remapping in OpenLogi using TOML files. Customize global defaults and device overrides for shortcuts and commands efficiently.

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

---

**OpenLogi stores button remappings in a TOML configuration file at `~/.config/openlogi/config.toml`, using a hierarchical structure that supports global defaults and per-device overrides defined by HID++ button codes mapped to keyboard shortcuts, commands, or other actions.**

OpenLogi is an open-source driver for Logitech HID++ devices that enables deep hardware customization through declarative configuration. The button remapping system is implemented in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), which parses user-defined TOML tables into a `Config` struct containing a `button_remap` field that maps device identifiers to lists of input translations.

## Configuration File Location

OpenLogi creates the configuration file at `~/.config/openlogi/config.toml` by default on Linux and macOS systems. To verify the exact path on your system, run the `openlogi --config-path` command in your terminal.

The configuration is loaded once at agent startup. The parser validates the TOML structure and builds an internal lookup table that the HID++ driver uses to intercept button events and synthesize replacement inputs.

## Global vs. Per-Device Remapping Hierarchy

The `button_remap` configuration uses a two-tier hierarchy that allows you to define universal defaults and device-specific exceptions:

- **Global `[button_remap]`** – Applies to all connected Logitech devices unless overridden by a specific device entry
- **Device-specific `[[device.<device_id>.button_remap]]`** – Overrides or extends the global list for a specific vendor/product ID pair

When OpenLogi processes input events, it checks for device-specific mappings first. If no match exists, it falls back to the global configuration. This hierarchy is enforced in the configuration loader at [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs).

## Button Mapping Syntax

Each remapping entry within the TOML arrays requires specific fields that define the input interception and output action:

- **`from`** – The HID++ button code to intercept (e.g., `button_5`, `button_8`)
- **`to`** – The target action, which can be a key combination like `Ctrl+Alt+T`, a media key like `media_play_pause`, or a system action like `mission_control`
- **`condition`** (optional) – An application-specific filter that limits the remap to when a specific app is foregrounded, identified by bundle ID (macOS) or executable name

The parser in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) validates these fields and ensures that `from` values correspond to valid HID++ button constants before registering them with the platform-specific input subsystems.

## Identifying Your Device ID

To configure per-device remappings, you must know your hardware's vendor ID (VID) and product ID (PID). OpenLogi provides a CLI command to discover these identifiers:

```bash
openlogi list

```

This command is implemented in [`crates/openlogi-cli/src/cmd/list.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-cli/src/cmd/list.rs) and outputs device strings in the format `VID:PID` (hexadecimal). Use this exact string in your TOML tables within the `[[device."VID:PID".button_remap]]` headers, such as `[[device."046d:c539".button_remap]]` for a Logitech G502 mouse.

## Platform-Specific Permissions

Button remapping requires low-level input access that varies by operating system:

**Linux** – The OpenLogi agent needs write access to `/dev/uinput` to synthesize key events. The project repository includes a `udev` rule that grants this permission to users in the `input` group. Without this access, the remapping logic in the core crate cannot inject the translated events.

**macOS** – The agent uses the event-tap system to intercept and replace button inputs. You must grant "Accessibility" permissions to the OpenLogi binary in System Preferences > Security & Privacy > Privacy > Accessibility. The code handles the event-tap registration in the platform-specific device layers.

## Reloading and Applying Changes

After editing [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml), you must reload the configuration for changes to take effect:

```bash
openlogi reload

```

Alternatively, restart the OpenLogi agent (`openlogi-agent`). The reload command triggers the configuration parser to rebuild the `button_remap` lookup tables without restarting the underlying HID++ connection handlers.

## Practical Configuration Examples

Here is a complete `~/.config/openlogi/config.toml` demonstrating global and per-device remappings:

```toml

# Global button remapping – applies to every device

[button_remap]
from = "button_5"
to   = "media_play_pause"

# Per-device remapping for Logitech G502 (VID:PID 046d:c539)

[[device."046d:c539".button_remap]]
from = "button_8"
to   = "Ctrl+Alt+T"

# Per-device remapping for Logitech MX Master with app-specific condition

[[device."046d:b012".button_remap]]
from = "button_4"
to   = "mission_control"
condition = { app = "com.google.Chrome" }

```

The first table defines a universal side-button remap. The subsequent arrays target specific hardware by their USB identifiers. For a comprehensive reference of all supported configuration options, see [`docs/config.example.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/config.example.toml) in the repository root.

## Summary

- OpenLogi reads button remappings from `~/.config/openlogi/config.toml`, parsed by [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs)
- Use `[button_remap]` for global defaults and `[[device."VID:PID".button_remap]]` for device-specific overrides
- Each mapping requires `from` (source button) and `to` (target action) fields, with an optional `condition` for app-specific activation
- Discover device IDs using `openlogi list` (source: [`crates/openlogi-cli/src/cmd/list.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-cli/src/cmd/list.rs))
- Linux requires `/dev/uinput` access via udev rules; macOS requires Accessibility permissions for event-tapping
- Apply changes with `openlogi reload` or by restarting the agent

## Frequently Asked Questions

### How do I find the correct button codes for my Logitech mouse?

Use the `openlogi list` command to display connected devices, then monitor the OpenLogi agent logs in debug mode. When you press physical buttons, the HID++ reports log the corresponding button codes (e.g., `button_4`, `button_8`) that you must use in the `from` field of your TOML configuration.

### Can I remap buttons to launch specific applications rather than keyboard shortcuts?

Yes. The `to` field accepts command strings in addition to key combinations. You can specify a shell command such as `to = "exec:firefox"` or `to = "exec:/usr/local/bin/myscript.sh"` depending on your platform. The agent executes these commands via the system shell when the button event triggers.

### Why aren't my button remaps working after editing the config file?

First, ensure you ran `openlogi reload` or restarted the agent. Then verify that the device ID in your TOML matches exactly (including case) with the output from `openlogi list`. Finally, check platform permissions: Linux users need write access to `/dev/uinput`, and macOS users must grant Accessibility permissions to the OpenLogi binary in System Security settings.

### Does OpenLogi support conditional remapping based on the active application?

Yes. Add a `condition` table to any mapping entry with an `app` field specifying the bundle identifier (macOS) or executable name (Linux). For example, `condition = { app = "com.apple.Terminal" }` restricts the remap to only activate when Terminal is the foreground application.