# How to Configure OpenLogi Using TOML Files: A Complete Guide

> Master OpenLogi configuration with our complete guide to TOML files. Learn to customize settings and ensure seamless application startup. Read now to optimize your OpenLogi experience.

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

---

**OpenLogi stores all user-customizable settings in a [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file located in the user’s configuration directory, which the application parses at startup using the `toml` crate and validates against schema version 6.**

The AprilNEA/OpenLogi project uses a hierarchical TOML configuration system defined in the **Config** module. When the application launches, it reads this file to resolve device settings, button mappings, and UI preferences into a fully populated `Config` struct that drives runtime behavior.

## Configuration File Location and Discovery

OpenLogi resolves the configuration path using platform-specific conventions implemented in [`crates/openlogi-core/src/paths.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/paths.rs).

On Linux, the file resides at:

```bash
$XDG_CONFIG_HOME/openlogi/config.toml

```

On macOS, the path is:

```bash
~/Library/Application Support/OpenLogi/config.toml

```

If the file is missing or contains parse errors, OpenLogi falls back to default values and writes a fresh [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) on exit. Errors during deserialization are logged with `warn!` macros, and the GUI disables config writes to prevent file corruption.

## Core Configuration Structure

The root of [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) requires a **schema version** identifier and supports optional global flags.

```toml
schema_version = 6
selected_device = "receiver:aabbccdd:slot:1"

[app_settings]
launch_at_login = true
language = "en"
ui_scale = "normal"

```

The `schema_version` field is mandatory and tells OpenLogi which parsing rules to apply. The current version is **6**. The `selected_device` key optionally forces the GUI to open a specific device on launch.

According to [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), the `[app_settings]` table controls global UI behavior, including launch-at-login preferences, localization, and interface scaling.

## Device-Specific Configuration

Per-device settings live under `[devices.<id>]` tables. The `<id>` format depends on your schema version:

- **Legacy (pre-schema 5)**: Uses receiver routes like `receiver:aabbccdd:slot:1`
- **Schema 5+**: Uses unit identifiers like `unit:6be9d300`

```toml
[devices."receiver:aabbccdd:slot:1"]
dpi = 1600
dpi_presets = [800, 1600, 3200]

```

Schema 5 introduces **link-specific overrides** via the `links` subtable, allowing you to customize settings when a device connects through specific USB paths:

```toml
[devices."unit:6be9d300".links."direct:046d:c08d".overrides]
dpi = 800

```

## Button Bindings and Action Mappings

The `bindings` table under each device defines button-to-action mappings. OpenLogi supports three descriptor types:

1. **Simple strings** for predefined actions
2. **`CustomShortcut`** objects for complex key combinations
3. **`HoldShortcut`** descriptors for time-based triggers

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

```

The binding logic is implemented across `crates/openlogi-core/src/binding/*.rs`, where the parser validates action names and shortcut syntax.

## Per-Application and Advanced Overrides

**Per-application bindings** override global mappings only when specific applications are focused. Define these under `per_app_bindings.<bundle_id>`:

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

```

Additional specialized modules include:

- **`action_ring`**: Configures the on-screen Actions Ring, including haptics and slot definitions
- **`lighting`**: Controls LED color and brightness
- **`smartshift`**: Manages SmartShift/DPI-shift behavior, including torque settings and auto-disengage modes
- **`keyboard.bindings`**: Global shortcut remappings that apply system-wide regardless of device

**Action Ring example:**

```toml
[devices."receiver:aabbccdd:slot:1".action_ring.default.slots]
Top = { action = "Copy", icon = "Keyboard" }
Right = { action = { OpenApplication = { path = "/Applications/Safari.app", display_name = "Safari" } }, icon = "Applications" }

```

## Error Handling and Validation

When [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs) reads the TOML file, it validates the structure against the declared schema version. Deserialization errors trigger fallback behavior:

- Missing files generate default configurations
- Parse errors log warnings but do not crash the application
- The GUI enters a read-only mode to prevent corruption of invalid files

The `Config` struct merges hierarchical settings—global defaults, per-device configurations, link-specific overrides, and per-app bindings—into a single resolved state consumed by the rest of the system.

## Summary

- OpenLogi reads settings from [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) in the user’s configuration directory, resolved via [`crates/openlogi-core/src/paths.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/paths.rs)
- The **schema version** (currently 6) determines parsing rules and device identifier formats
- Device configurations support both receiver routes (`receiver:...`) and unit IDs (`unit:...`), with schema 5+ enabling link-specific overrides
- **Bindings** support simple strings, `CustomShortcut`, and `HoldShortcut` descriptors for button remapping
- **Per-application bindings** allow context-sensitive overrides based on the focused application
- Invalid configurations trigger safe fallbacks and logging via `warn!` macros without crashing the application

## Frequently Asked Questions

### Where does OpenLogi store the configuration file on Windows?

The raw source analysis focuses on Linux and macOS paths. However, following XDG conventions, Windows typically uses `%APPDATA%\OpenLogi\config.toml` or `%LOCALAPPDATA%\OpenLogi\config.toml`, though you should verify the exact path in [`crates/openlogi-core/src/paths.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/paths.rs) for your specific release.

### What happens if I omit the schema_version field?

OpenLogi requires `schema_version` to determine which parsing rules to apply. If this field is missing or contains an unsupported version, the configuration parser will fail to deserialize the file, trigger a warning log, and fall back to default settings.

### Can I mix receiver-based and unit-based device IDs in the same file?

While technically possible if upgrading configurations, you should maintain consistency within your schema version. Schema 5+ prefers `unit:...` identifiers, while older schemas use `receiver:...` routes. Mixing formats without proper link tables may cause device recognition issues.

### Why are my button bindings not applying to specific applications?

Ensure you are using the correct bundle identifier under `per_app_bindings` (e.g., `com.microsoft.VSCode` rather than "VS Code"). The matching logic in `crates/openlogi-core/src/binding/*.rs` requires exact bundle ID strings to trigger per-application overrides.