# How to Configure Per-Application Action Ring Layouts in OpenLogi

> Learn to configure per-application action ring layouts in OpenLogi using TOML. Customize slot configurations based on process names or bundle identifiers for tailored app control.

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

---

**OpenLogi allows you to override the global Action Ring layout on a per-application basis by defining `per_app_bindings` sections in your TOML configuration file, using either process names or bundle identifiers to trigger application-specific slot configurations.**

OpenLogi is an open-source input device manager that enables advanced customization of Logitech HID++ devices. When you configure per-application action ring layouts in OpenLogi, the software automatically switches between different button mappings whenever you change foreground applications, providing context-sensitive control schemes tailored to your workflow.

## Understanding the Configuration Hierarchy

OpenLogi stores user configuration in a TOML file located at `$HOME/.config/openlogi/config.toml`. The configuration uses a hierarchical structure organized by physical devices and slots.

Each device is identified by a **receiver serial number** and **slot number** pair, formatted as `receiver:<serial>:slot:<n>`. Within each device section, you can define both global defaults and application-specific overrides.

The configuration hierarchy follows this pattern:

```

[devices."receiver:<serial>:slot:<n>"]                            # Device root

[devices."receiver:<serial>:slot:<n>".action_ring]                  # Global layout

default.slots = ["action1", "action2", ...]                        # 8 required slots

[devices."receiver:<serial>:slot:<n>".per_app_bindings."<app-id>"]  # App-specific

action_ring = { slots = ["app-action1", ...] }                     # Override layout

```

## Defining Global vs. Per-Application Action Rings

The **global Action Ring** serves as the default layout when no specific application binding matches the current foreground window. You define this using the `action_ring.default.slots` array, which must contain exactly eight action entries.

**Per-application bindings** override this global layout when specific conditions are met. The `<app-id>` key identifies target applications using one of two formats:

- **Process name format**: `exe:sharex.exe` for Windows executables
- **Bundle identifier format**: `com.microsoft.VSCode` for macOS and Linux applications

When the foreground application changes, OpenLogi's desktop process queries the current window, matches it against the `per_app_bindings` table, and activates the corresponding ring layout.

## Step-by-Step Configuration Examples

### Linux and macOS Application Bundles

To configure a per-application layout for Visual Studio Code on macOS or Linux, identify the bundle identifier and create a nested configuration section:

```toml
[devices."receiver:aabbccdd:slot:1"]

# Global default layout

[devices."receiver:aabbccdd:slot:1".action_ring]
default.slots = [
    "Zoom", "Scroll", "DPI+", "DPI-", "Button1", "Button2", "Button3", "Button4"
]

# Per-application override for VS Code

[devices."receiver:aabbccdd:slot:1".per_app_bindings."com.microsoft.VSCode"]
action_ring = { slots = [
    "Button5", "Button6", "Zoom", "Scroll", "DPI+", "DPI-", "Button1", "Button2"
] }

```

### Windows Executable Names

For Windows applications, use the `exe:` prefix followed by the executable filename. This example configures ShareX with a customized action set:

```toml
[devices."receiver:11223344:slot:2".per_app_bindings."exe:sharex.exe"]
action_ring = { slots = [
    "Button3", "Button4", "Button5", "Button6", "Zoom", "Scroll", "DPI+", "DPI-"
] }

```

After saving your configuration file, restart OpenLogi or execute `openlogi reload` to apply the changes without restarting the system service.

## How the Configuration is Applied

According to the OpenLogi source code, the per-application ring configuration flows through several specialized crates:

1. **Parsing**: The `openlogi-core` crate reads the TOML file in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs), deserializing the configuration into `DeviceConfig` structs that contain `ActionRingConfig` and `PerAppBinding` definitions.

2. **Resolution**: When the foreground application changes, `openlogi-desktop` (specifically in [`crates/openlogi-desktop/src/features/profiles.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/features/profiles.rs)) performs the lookup against `per_app_bindings` using the current window's identifier.

3. **Communication**: The desktop process transmits the new ring layout to the agent process via the IPC contract defined in [`crates/openlogi-ipc/src/ipc.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-ipc/src/ipc.rs).

4. **Device Programming**: The agent receives the configuration in [`crates/openlogi-agent/src/server.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-agent/src/server.rs) and programs the HID++ device hardware with the new slot assignments.

5. **UI Rendering**: Simultaneously, [`crates/openlogi-overlay/src/ring.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-overlay/src/ring.rs) renders the on-screen Action Ring display to reflect the current active layout.

## Summary

- OpenLogi uses a TOML configuration file at `$HOME/.config/openlogi/config.toml` to store device settings.
- Per-application action rings are defined under `[devices."receiver:<serial>:slot:<n>".per_app_bindings."<app-id>"]` sections.
- Application identifiers use either `exe:filename.exe` for Windows or bundle identifiers like `com.company.App` for Unix systems.
- Each `action_ring.slots` array must contain exactly eight action entries to match the hardware's physical ring capacity.
- Changes require a configuration reload or service restart to take effect.
- The implementation spans multiple crates: `openlogi-core` for parsing, `openlogi-desktop` for window detection, `openlogi-ipc` for process communication, and `openlogi-agent` for hardware programming.

## Frequently Asked Questions

### How does OpenLogi identify applications for per-app bindings?

OpenLogi supports two identification methods based on the operating system. On Windows, use the `exe:` prefix followed by the executable name (e.g., `exe:photoshop.exe`). On macOS and Linux, use the application's bundle identifier (e.g., `com.adobe.Photoshop`). This matching occurs in [`crates/openlogi-desktop/src/features/profiles.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-desktop/src/features/profiles.rs), which queries the active window manager to retrieve the current foreground process identifier.

### Can I mix different action types in the same ring layout?

Yes, the `slots` array accepts any valid OpenLogi action identifiers. Common actions include `Scroll`, `Zoom`, `DPI+`, `DPI-`, and various `Button#` mappings. You can combine these freely within the eight-slot array to create context-specific workflows, such as assigning zoom controls to graphic design applications while reserving media controls for video players.

### What happens if the foreground application has no specific binding configured?

When no matching entry exists in the `per_app_bindings` table for the current foreground application, OpenLogi falls back to the global configuration specified in `action_ring.default.slots`. This ensures that devices always maintain a functional layout even when switching to applications without custom profiles.

### How many per-application bindings can I define for a single device?

There is no explicit limit enforced by the configuration parser in [`crates/openlogi-core/src/config.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config.rs). You can define as many `per_app_bindings` entries as needed within a single device section, allowing you to create distinct layouts for every application in your workflow. Each binding exists as a separate table entry under the device section's `per_app_bindings` namespace.