How to Configure Per-Application Profile Bindings for Automatic App Switching in OpenLogi
OpenLogi configures per-application profile bindings by monitoring the active foreground window and dynamically overlaying device-specific settings—keyed by application bundle identifier—onto the global profile whenever the target application gains focus.
OpenLogi enables sophisticated device management through per-application profiles that apply custom button mappings, DPI adjustments, and lighting configurations only when specific applications are active. To configure per-application profile bindings for automatic app switching in OpenLogi, you define device-specific overrides in a TOML configuration file or through the desktop interface, allowing the agent core to dynamically swap bindings based on the current foreground window's identity.
Understanding Per-Application Profile Storage
OpenLogi persists configuration data in a TOML file located at ~/.config/openlogi/config.toml. According to the data model defined in crates/openlogi-core/src/config.rs, each device entry contains both a global profile and an optional map of per-app profiles keyed by the application's bundle identifier.
Within the configuration structure, the per_app section uses the bundle identifier as the lookup key. Each per-application profile contains the same sections as the global profile—buttons, DPI, lighting, and other device settings—but only defined entries override the global defaults. Unspecified properties inherit from the global profile, creating a layered configuration system.
How OpenLogi Detects and Matches Foreground Applications
The automatic switching mechanism relies on three core components working in concert:
- Foreground detection – Platform-specific hooks in
crates/openlogi-hook/src/lib.rsexpose the current foreground application throughForegroundApp::id, which returns the identifier used for profile lookups. - Profile resolution – The IPC layer defined in
crates/openlogi-ipc/src/ipc.rstransmits the active application identifier from the background agent to the GUI process. - Per-app overlay – When a new foreground application is detected, the orchestrator in
crates/openlogi-agent-core/src/orchestrator.rsrecomputes the effective binding map by merging the per-app profile on top of the global profile.
This architecture ensures that profile switches occur immediately when the user changes applications, with zero manual intervention required.
Setting Up Per-Application Profile Bindings
Editing the TOML Configuration Directly
To manually configure per-application profile bindings, edit the TOML configuration file and add a per_app section under the target device. The following example demonstrates profiles for Safari and VS Code:
[[devices]]
name = "Logitech G502"
[devices.profile]
# Global defaults apply when no per-app profile matches
[devices.per_app."com.apple.Safari"]
[devices.per_app."com.apple.Safari".profile]
button_1 = { action = "copy" }
dpi = 800
[devices.per_app."com.microsoft.VSCode"]
[devices.per_app."com.microsoft.VSCode".profile]
button_2 = { action = "run_debug" }
button_3 = { action = "toggle_terminal" }
The configuration parser in crates/openlogi-core/src/config.rs validates these entries and loads them into the device state manager. Keys inside devices.per_app."<bundle_id>" mirror the structure of the global profile section, allowing granular control over individual settings.
Using the Desktop Interface
Alternatively, use the desktop UI to create per-application profiles without manually editing TOML:
- Open the Profiles pane (ring icon) in the OpenLogi desktop application.
- Click Add Profile → Per-App and select the target application from the installed apps list.
- Adjust bindings in the panel; changes persist automatically to the configuration file.
The UI logic for this workflow resides in crates/openlogi-desktop/src/state/bindings.rs, which handles the creation, editing, and deletion of per-app binding entries.
Verifying Automatic Switching
To confirm that automatic app switching functions correctly:
- Launch the target application (e.g., Safari with bundle ID
com.apple.Safari). - Observe the OpenLogi GUI header, which displays the active profile name via
state.active_profile_name()incrates/openlogi-desktop/src/state.rs. - Test the configured buttons; the agent routes inputs according to the per-app overrides rather than the global profile.
The system applies changes immediately upon detecting the foreground window change, requiring no restart of the OpenLogi agent.
Handling Edge Cases and Configuration Pitfalls
When working with per-application profiles, consider the following scenarios:
- No matching per-app profile – The system falls back to the global profile. Ensure you define a global profile to guarantee consistent default behavior.
- Applications without bundle identifiers – On some Linux window managers, applications may lack standard identifiers, causing per-app lookup to fail. In these cases, the system remains on the global profile; you may manually specify fallback identifiers in the configuration if needed.
- Multiple devices with overlapping profiles – Each device resolves its per-app overlay independently. Device configurations remain isolated, so a profile activated for your mouse does not affect your keyboard settings.
- Empty profile pruning – If you remove all bindings from a per-app profile, the UI logic in
crates/openlogi-desktop/src/state/bindings.rsprunes the empty entry on save, reverting to global defaults. Use the delete function in the UI rather than manual file editing to remove profiles cleanly.
Programmatic Configuration Examples
Creating Bindings in Rust
For advanced integrations, manipulate per-application profiles programmatically using the openlogi_core crate:
use openlogi_core::config::{Config, DeviceKey, Profile};
use std::path::PathBuf;
// Load existing configuration from ~/.config/openlogi/config.toml
let mut cfg = Config::load().expect("cannot read config");
// Identify the target device and application
let dev_key = DeviceKey::new("Logitech G502");
let app_id = "com.apple.Safari".to_string();
// Create or modify the per-app profile entry
let per_app = cfg
.devices
.entry(dev_key.clone())
.or_default()
.per_app
.entry(app_id.clone())
.or_insert_with(Profile::default);
// Assign a custom button binding
per_app.profile.button_1 = Some(openlogi_core::bindings::Binding::Single(
openlogi_core::actions::Action::Custom("copy".into()),
));
// Write changes to disk
cfg.save().expect("cannot write config");
This approach directly interfaces with the configuration API defined in crates/openlogi-core/src/config.rs, bypassing the UI while maintaining TOML compatibility.
CLI Profile Management
For automation or shell scripting, use the OpenLogi CLI to set per-application bindings:
openlogi-cli profile set --device "Logitech G502" \
--app "com.apple.Safari" --binding "button_1=copy"
The CLI implementation is located in crates/openlogi-cli/src/cmd/light.rs and related command modules, providing programmatic access to the same configuration structures used by the GUI.
Summary
- OpenLogi stores per-application profiles in
~/.config/openlogi/config.tomlas overlays on the global profile, parsed bycrates/openlogi-core/src/config.rs. - The system uses
ForegroundApp::idfromcrates/openlogi-hook/src/lib.rsto detect the active application and trigger automatic profile switching. - Configure bindings manually via TOML editing or through the desktop UI in
crates/openlogi-desktop/src/state/bindings.rs. - The orchestrator in
crates/openlogi-agent-core/src/orchestrator.rsmerges per-app settings with global defaults at runtime. - Use the Rust API or CLI tools for automated profile management and deployment.
Frequently Asked Questions
What happens if an application does not have a bundle identifier?
When the foreground application lacks a bundle identifier—common with some Linux window managers or Wine applications—the lookup mechanism in crates/openlogi-hook/src/lib.rs fails to return a valid key. In this scenario, OpenLogi retains the current global profile and does not attempt to apply per-app overrides. You can work around this limitation by manually specifying an identifier in the configuration file if the application exposes any consistent metadata.
Can different devices have conflicting per-app profiles for the same application?
Yes, and these conflicts do not cause errors. Each device entry in the TOML configuration maintains its own per_app map, and the orchestrator in crates/openlogi-agent-core/src/orchestrator.rs processes each device independently. This means your mouse can activate a gaming profile for an application while your keyboard maintains a productivity layout for the same app, with neither configuration interfering with the other.
How do I temporarily disable automatic app switching for a specific device?
To disable automatic switching, remove all per-app entries for that device from the configuration file, or delete the per-app profile through the desktop UI. When no per-app profiles exist for a device, the system consistently uses the global profile defined in devices.profile. The cleanup logic in crates/openlogi-desktop/src/state/bindings.rs automatically removes empty per-app entries when you save changes through the interface.
Why does the GUI not show my newly created per-app profile immediately?
The GUI relies on the IPC layer in crates/openlogi-ipc/src/ipc.rs to receive foreground application updates from the agent. If the profile does not appear, ensure the OpenLogi agent process is running and that the application you configured is actually the foreground window. The display updates when state.active_profile_name() in crates/openlogi-desktop/src/state.rs detects a change in the active application identifier.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →