How OpenLogi Implements Automatic Per-App Profile Switching Based on App Focus

OpenLogi achieves automatic per-app profile switching by monitoring OS-level window focus events through platform-specific hooks, transmitting application identifiers via tarpc IPC to a background agent, and performing exact-match profile lookups that trigger immediate HID++ device reconfiguration.

OpenLogi is an open-source device management platform that enables context-aware input customization for Logitech peripherals. The project's automatic per-app profile switching based on app focus relies on a three-tier architecture that bridges operating system notifications with hardware configuration. This implementation ensures your mouse bindings, DPI settings, and lighting profiles automatically adapt the moment you switch between applications.

The Three-Tier Architecture

The system consists of three tightly-coupled components that operate across separate processes. The OS Hook listens for window-focus events and extracts stable application identifiers. An IPC Layer transmits these identifiers from the hook process to the background agent using a versioned tarpc protocol. Finally, the Agent Core receives notifications, resolves per-app profiles from the user's TOML configuration, and activates the corresponding device settings.

Platform-Specific Focus Detection

OpenLogi implements native focus listeners for each supported operating system to ensure reliable foreground application detection.

macOS Focus Monitoring

On macOS, the hook registers for NSWorkspaceDidActivateApplicationNotification in crates/openlogi-hook/src/macos.rs around line 640. When the notification fires, the system calls ForegroundApp::from_ns_workspace to construct a stable identifier containing the application's bundle ID and user-friendly name.

Linux Focus Monitoring (X11 and WLRoots)

For Linux environments, the hook monitors two distinct windowing systems. Under X11, it reads the _NET_ACTIVE_WINDOW property from the root window, as implemented in crates/openlogi-hook/src/linux.rs near line 546. For WLRoots-based compositors, it listens to "foreign-toplevel" events. Both paths normalize the window title or executable path before constructing the identical ForegroundApp structure used across platforms.

IPC Communication and Message Flow

When the OS hook detects a focus change, it must communicate with the persistent background agent. The hook calls the set_foreground_app RPC defined in crates/openlogi-ipc/src/ipc.rs between lines 140 and 165. This tarpc-based IPC contract transmits the ForegroundApp struct over a local socket, ensuring versioned, forward-compatible communication between the hook and agent processes.

Profile Resolution and Device Activation

Once the agent receives the foreground application notification, it executes a precise resolution and application sequence.

Exact-Match Profile Lookup

The agent maintains a map of per-app profiles keyed by the stable identifier extracted earlier. In crates/openlogi-core/src/app.rs between lines 27 and 46, the system performs an exact-match lookup using profiles.get(&foreground.id). If the identifier matches a configured profile key, the agent swaps the active profile from the global default to the application-specific configuration.

Applying HID++ Settings

The resolved profile contains HID++ configuration parameters including button bindings, DPI levels, lighting effects, and smart-shift settings. The agent writes these settings to the physical device through the openlogi-device crate, which abstracts the HID++ communication protocol. This occurs without user interaction, creating the seamless "automatic" switching experience.

Configuring Per-Application Profiles

Users define application-specific profiles in the TOML configuration file using stable platform identifiers.

Defining a Safari Profile on macOS

The following configuration automatically activates when Safari becomes the front-most application:


# ~/.config/openlogi/config.toml

[profile."com.apple.Safari"]

# Example bindings for Safari

[[binding]]
type = "button"
button = 1
action = "keypress"
key = "Cmd+L"

[dpi]
default = 1200

The profile key com.apple.Safari corresponds to the bundle identifier supplied by the macOS hook, ensuring exact matching when the app gains focus.

Querying the Active Profile via CLI

You can verify which profile is currently active based on the focused application using the command-line interface:

openlogi-cli profile show-active

This command queries the agent via the same IPC channel and displays the active profile name, confirming that the automatic switching is functioning correctly.

Programmatic Profile Control

Developers can manually trigger profile switches by mimicking the OS hook's behavior through the IPC client:

use openlogi_ipc::client::OpenLogiClient;
use openlogi_core::app::ApplicationTarget;

async fn switch_to_app(app_path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let client = OpenLogiClient::connect().await?;
    let target = ApplicationTarget::new(app_path, "").unwrap();
    client.set_foreground_app(target).await?;
    Ok(())
}

Calling set_foreground_app forces the agent to load the corresponding per-app profile regardless of actual OS focus state.

Summary

  • OS-level hooks in crates/openlogi-hook/src/macos.rs and linux.rs monitor NSWorkspaceDidActivateApplicationNotification and _NET_ACTIVE_WINDOW events to detect foreground applications.
  • Tarpc IPC transmits ForegroundApp structures from the hook process to the background agent via the set_foreground_app RPC.
  • Exact-match resolution in the agent core lookups profiles by stable identifiers (bundle IDs on macOS, executable paths on Linux).
  • Immediate application of HID++ settings occurs through the openlogi-device crate without requiring manual intervention.
  • Configuration keys must match platform-specific identifiers exactly for automatic switching to activate.

Frequently Asked Questions

How does OpenLogi identify which application is currently focused?

OpenLogi uses platform-specific mechanisms to extract stable identifiers. On macOS, it captures the bundle identifier from NSWorkspaceDidActivateApplicationNotification in crates/openlogi-hook/src/macos.rs. On Linux, it monitors the _NET_ACTIVE_WINDOW X11 property or WLRoots foreign-toplevel events in crates/openlogi-hook/src/linux.rs, extracting the executable path or window class.

What happens if no specific profile exists for the focused application?

If the agent performs an exact-match lookup and finds no profile matching the current ForegroundApp identifier, it retains the global default profile. The system does not attempt fuzzy matching, ensuring predictable behavior and preventing unintended profile activation when switching to unconfigured applications.

Can I manually trigger a profile switch without changing application focus?

Yes. The IPC layer exposed in crates/openlogi-ipc/src/ipc.rs allows programmatic control. By creating an ApplicationTarget and calling set_foreground_app through the OpenLogiClient, you can force the agent to load any configured profile immediately, bypassing the OS hook entirely.

Why does OpenLogi use exact-match rather than regex or pattern matching for profile identification?

Exact-match lookup using stable identifiers like bundle IDs ensures deterministic, reproducible behavior across system restarts and application updates. This approach eliminates ambiguity that could arise from regex patterns matching multiple applications or window titles changing dynamically, guaranteeing the correct profile loads every time for a specific executable.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →