# How to Set Up Per-App Profiles in OpenLogi: A Complete Configuration Guide

> Master OpenLogi's per-app profiles! Learn to automatically switch button bindings and Action-Ring layouts based on foreground applications with this comprehensive guide.

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

---

**OpenLogi stores per-application button bindings and Actions-Ring layouts in the [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file under each device’s `per_app_bindings` and `action_ring.apps` tables, automatically switching profiles based on the foreground application’s identifier.**

OpenLogi is an open-source input device manager that enables context-aware automation through per-application configuration profiles. Setting up per-app profiles in OpenLogi allows you to override global button mappings and customize the eight-slot radial Actions-Ring menu depending on which application is currently in focus. These configurations are defined in a single TOML file—documented in [`docs/CONFIGURATION.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/CONFIGURATION.md) and parsed by the core library—that both the GUI and background agent read simultaneously.

## Understand the Per-App Profile Architecture

OpenLogi implements per-app customization through two distinct but related mechanisms in the core configuration structures. According to the OpenLogi source code, the system uses specialized data structures to map application identifiers to specific input behaviors.

### Core Data Structures

The `DeviceConfig` struct in [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs) defines two critical fields for per-app behavior:

- **`per_app_bindings`**: A `BTreeMap<String, BTreeMap<ButtonId, Action>>` that maps application identifiers to button-specific action overrides. The outer map uses the app identifier as a key, while the inner map associates specific button IDs with their corresponding actions.

- **`action_ring`**: An `ActionRingConfig` structure that supports both global defaults and per-application overrides, allowing each app to have its own eight-slot radial layout.

## Locate Your Configuration File

Before editing per-app profiles, you must locate the [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file that OpenLogi uses for persistence. The file location varies by platform:

- **macOS and Linux**: `~/.config/openlogi/config.toml`
- **Windows**: `%USERPROFILE%\.config\openlogi\config.toml`

Alternatively, you can open the file directly through the GUI by navigating to **Settings → Open Config**. This file is shared between the desktop application and the background agent, so changes made manually or through the GUI affect both components simultaneously.

## Identify Application Keys

Per-app profiles rely on platform-specific identifiers to detect the foreground application. You must use the correct key format for your operating system:

- **macOS**: Use the bundle identifier, such as `com.microsoft.VSCode` or `org.mozilla.firefox`.
- **Linux (X11/Wayland)**: The system falls back to the X11 `WM_CLASS` string. On Wayland, this requires XWayland to function correctly, as noted in [`docs/INSTALL-linux.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/INSTALL-linux.md).
- **Windows**: Use the exact lower-cased executable path or the shortcut format `exe:<filename>.exe`.

## Configure Per-App Button Bindings

To create per-app button overrides, define a `per_app_bindings` table inside your device configuration. Each entry maps an application identifier to specific button remappings that take precedence over global bindings when that application is active.

Add the following structure to your [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml) file:

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

# Global fallback bindings

bindings = { Button5 = "Back", Button6 = "Forward" }

# Per-application overrides

[devices."receiver:12345:slot:1".per_app_bindings."com.microsoft.VSCode"]
Button5 = "Copy"
Button6 = "Paste"

[devices."receiver:12345:slot:1".per_app_bindings."org.mozilla.firefox"]
Button5 = "Back"
Button6 = "Forward"

```

In this example, when Visual Studio Code is active, Button5 triggers Copy and Button6 triggers Paste. When Firefox is active, the buttons revert to standard navigation functions. If neither app is focused, the global bindings apply.

## Configure Per-App Actions-Ring Layouts

Beyond button remapping, you can define complete Actions-Ring layouts on a per-application basis using the `action_ring.apps` sub-table. This allows each application to display its own set of eight contextual actions in the radial menu.

Configure per-app ring layouts as follows:

```toml
[devices."receiver:12345:slot:1".action_ring]

# Global ring configuration (fallback)

Top = { action = "Copy", icon = "Keyboard" }
Bottom = { action = "Paste", icon = "Keyboard" }

# Per-application ring layouts

[devices."receiver:12345:slot:1".action_ring.apps."com.microsoft.VSCode"]
Top = { action = "ShowTerminal", icon = "Terminal" }
Bottom = { action = "ShowExplorer", icon = "Folder" }

[devices."receiver:12345:slot:1".action_ring.apps."org.mozilla.firefox"]
Top = { action = "Back", icon = "ArrowLeft" }
Bottom = { action = "Forward", icon = "ArrowRight" }

```

When the foreground application matches a key in the `apps` map, OpenLogi displays the corresponding layout. If no match exists, the system falls back to the global `action_ring` configuration defined at the device level.

## Reload and Verify Your Configuration

After editing [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml), you must ensure the system recognizes your changes.

1. **Automatic Reload**: The GUI automatically detects file changes when you save [`config.toml`](https://github.com/AprilNEA/OpenLogi/blob/main/config.toml).
2. **Manual Reload**: If you edit the file manually outside the GUI, either reopen the application or select **Reload Config** from the tray menu to force the agent to re-read the configuration.

To verify a per-app profile is active:

1. Switch to the target application.
2. Open the Actions-Ring using your configured `ShowActionsRing` shortcut. The layout should display your custom per-app slots.
3. Press a button with per-app bindings. The overridden action should execute instead of the global binding.

If the profile does not activate, double-check the application identifier against the format requirements for your platform and ensure the key exists in the `per_app_bindings` or `action_ring.apps` maps.

## Summary

OpenLogi implements per-app profiles through a flexible TOML-based configuration system that enables context-aware device behavior. Key implementation details include:

- **Storage Location**: All per-app configurations reside in `~/.config/openlogi/config.toml` (or the Windows equivalent), shared between GUI and agent.
- **Data Structures**: The `DeviceConfig` struct uses `BTreeMap<String, BTreeMap<ButtonId, Action>>` for bindings and `ActionRingConfig` with `apps` sub-tables for radial layouts, as defined in [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs).
- **Application Identification**: Profiles trigger using platform-specific keys including macOS bundle IDs, Linux `WM_CLASS` values, and Windows executable paths.
- **Inheritance Model**: Per-app definitions override global device settings when the foreground app matches; unmatched apps fall back to default configurations.
- **Runtime Application**: The desktop state management code in `crates/openlogi-desktop/src/state/` handles the dynamic application of these profiles during runtime.

## Frequently Asked Questions

### How does OpenLogi detect which application is currently active?

OpenLogi queries the operating system for the foreground window's identifier using platform-specific APIs. On macOS, it reads the bundle identifier; on Linux, it uses the X11 `WM_CLASS` property (requiring XWayland on Wayland compositors); and on Windows, it examines the executable path. This identifier is matched against keys in `per_app_bindings` and `action_ring.apps` to determine which configuration to apply.

### What happens if an app doesn't have a specific profile configured?

When the foreground application's identifier doesn't match any key in the per-app configuration tables, OpenLogi automatically falls back to the global settings defined in the device's root `bindings` and `action_ring` tables. This ensures continuous functionality while allowing selective customization only for specific applications.

### Can I configure per-app profiles on Linux Wayland without XWayland?

Currently, OpenLogi's per-app profile detection on Linux relies on X11 `WM_CLASS` detection, which requires XWayland for applications running natively on Wayland, as documented in [`docs/INSTALL-linux.md`](https://github.com/AprilNEA/OpenLogi/blob/main/docs/INSTALL-linux.md). Applications running without XWayland may not report the necessary window class information for profile switching to function correctly.

### Where in the source code are per-app profiles defined and processed?

The configuration schema resides in [`crates/openlogi-core/src/config/device.rs`](https://github.com/AprilNEA/OpenLogi/blob/main/crates/openlogi-core/src/config/device.rs), which defines the `DeviceConfig` struct containing `per_app_bindings` and `action_ring` fields. Runtime application of these profiles occurs in `crates/openlogi-desktop/src/state/`, where the system monitors foreground application changes and applies the corresponding button mappings and Actions-Ring layouts dynamically.